I've been developing JavaScript for a few years and I don't understand the fuss about promises at all.
It seems like all I do is change:
api(function(result){ api2(function(result2){ api3(function(result3){ // do work }); }); });
Which I could use a library like async for anyway, with something like:
api().then(function(result){ api2().then(function(result2){ api3().then(function(result3){ // do work }); }); });
Which is more code and less readable. I didn't gain anything here, it's not suddenly magically 'flat' either. Not to mention having to convert things to promises.
So, what's the big fuss about promises here?
Promise constructor takes only one argument which is a callback function (and that callback function is also referred as anonymous function too). Perform operations inside the callback function and if everything went well then call resolve.
A promise is a returned object where you attach callbacks, instead of passing callbacks into a function. the place where you attach the callback after a successful completion of a task is called, . then(). inside this you pass a callback through.
How would you answere these questions in an interview? The superiority of promises over callbacks is all about trust and control. Let me explain. We generally need to use callbacks (or promises) when there is a slow process (that's usually IO-related) that we need to perform without blocking the main program process.
It is fair to say promises are just syntactic sugar. Everything you can do with promises you can do with callbacks. In fact, most promise implementations provide ways of converting between the two whenever you want.
Promises are not callbacks. A promise represents the future result of an asynchronous operation. Of course, writing them the way you do, you get little benefit. But if you write them the way they are meant to be used, you can write asynchronous code in a way that resembles synchronous code and is much more easy to follow:
api().then(function(result){ return api2(); }).then(function(result2){ return api3(); }).then(function(result3){ // do work });
Certainly, not much less code, but much more readable.
But this is not the end. Let's discover the true benefits: What if you wanted to check for any error in any of the steps? It would be hell to do it with callbacks, but with promises, is a piece of cake:
api().then(function(result){ return api2(); }).then(function(result2){ return api3(); }).then(function(result3){ // do work }).catch(function(error) { //handle any error that may occur before this point });
Pretty much the same as a try { ... } catch
block.
Even better:
api().then(function(result){ return api2(); }).then(function(result2){ return api3(); }).then(function(result3){ // do work }).catch(function(error) { //handle any error that may occur before this point }).then(function() { //do something whether there was an error or not //like hiding an spinner if you were performing an AJAX request. });
And even better: What if those 3 calls to api
, api2
, api3
could run simultaneously (e.g. if they were AJAX calls) but you needed to wait for the three? Without promises, you should have to create some sort of counter. With promises, using the ES6 notation, is another piece of cake and pretty neat:
Promise.all([api(), api2(), api3()]).then(function(result) { //do work. result is an array contains the values of the three fulfilled promises. }).catch(function(error) { //handle the error. At least one of the promises rejected. });
Hope you see Promises in a new light now.
Yes, Promises are asynchronous callbacks. They can't do anything that callbacks can't do, and you face the same problems with asynchrony as with plain callbacks.
However, Promises are more than just callbacks. They are a very mighty abstraction, allow cleaner and better, functional code with less error-prone boilerplate.
So what's the main idea?
Promises are objects representing the result of a single (asynchronous) computation. They resolve to that result only once. There's a few things what this means:
Promises implement an observer pattern:
return
a Promise objectPromises are chainable (monadic, if you want):
.then()
method. It will take a callback to be called with the first result, and returns a promise for the result of the promise that the callback returns.Sounds complicated? Time for a code example.
var p1 = api1(); // returning a promise var p3 = p1.then(function(api1Result) { var p2 = api2(); // returning a promise return p2; // The result of p2 … }); // … becomes the result of p3 // So it does not make a difference whether you write api1().then(function(api1Result) { return api2().then(console.log) }) // or the flattened version api1().then(function(api1Result) { return api2(); }).then(console.log)
Flattening does not come magically, but you can easily do it. For your heavily nested example, the (near) equivalent would be
api1().then(api2).then(api3).then(/* do-work-callback */);
If seeing the code of these methods helps understanding, here's a most basic promise lib in a few lines.
What's the big fuss about promises?
The Promise abstraction allows much better composability of functions. For example, next to then
for chaining, the all
function creates a promise for the combined result of multiple parallel-waiting promises.
Last but not least Promises come with integrated error handling. The result of the computation might be that either the promise is fulfilled with a value, or it is rejected with a reason. All the composition functions handle this automatically and propagate errors in promise chains, so that you don't need to care about it explicitly everywhere - in contrast to a plain-callback implementation. In the end, you can add a dedicated error callback for all occurred exceptions.
Not to mention having to convert things to promises.
That's quite trivial actually with good promise libraries, see How do I convert an existing callback API to promises?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With