Callbacks have numerous problems, though, out of which the most severe one is probably the phenomenon of “marching to the right”:
When using the (still) common style of providing a callback as the last argument to a function initiating an asynchronous operation, you get this annoying result of ever-increasing indentation as you chain those operations together. It feels like the language itself is telling you that it was not designed for such a complex stuff… Coincidence? ;)
But it gets worse. Operations may fail somewhere along the way, which is something you’d probably like to know about. Depending on the conventions your project, framework or environment uses, this could mean additional boilerplate inside the callbacks to distinguish success from error cases. This is typical in Node.js, where first argument of callback represents the error, if any:
Alternatively, you may be asked to provide the error handler separately; an “errback”, as it’s sometimes called. Splitting the code into small parts is great and everything, but here it means you’ll have two functions as arguments:
Thankfully, all this uphill struggle is not merely the language’s fault. What’s really flawed here is any API that blends data for long-running operations with callbacks that process their results. These two aspects should be kept separate, especially because the latter can be handled so much better.
Meet promises. Promise is an object that encapsulates the status of a time-consuming operation and allows callbacks to be attached to it. It may not sound very impressive (or even appear as unnecessary abstraction), but the advantages go way beyond simply making the code more clear.
All examples here are using jQuery, but the ideas (or even API) are quite similar across the board.
Here’s a the first one, which should be both simple and useful:
Loading an image, especially over the network, is surely a task best done asynchronously. But there is no explicit
callback argument here, so how can we get the result once download completes?
No worries. This is exactly what the promise object is for, and we get it as a result from the above function. Rather than passing callback as the second argument, we can now attach it to the promise:
Similarly, we can specify an error handler for it:
or even both callback and “errback”, all while remaining neat and readable:
This is, in a nutshell, probably the simplest possible use of jQuery’s Promise. If you look into the
loadImage function, however, you will notice that we’re mostly operating on a different type of entity: the
$.Deferred is the jQuery-specific Deferred object, which acts as a “controller” for promise. Unlike the
jQuery.Promise we return from our function, the Deferred object allows to change the state of the operation it “wraps”. It can be
resolved – which indicates success – or it can be
rejected – which signifies failure. We use these two methods in
onerror handlers of
Image, thus “translating” them into the
fail callbacks on a Promise.
Moreover, Deferred also implements all the regular API of Promise. This is quite powerful feature that allows to mix additional logic into your asynchronous pipelines.
For example, let’s take an AJAX request; it may fail for network connectivity reasons, but you could also have your server respond with an error, and that would technically still be counted as success. We can fix that by intercepting the AJAX response early and
rejecting the Deferred object:
Callers of above function can chain more
done callbacks, but those will only be invoked if JSON returned in the response does not contain the
"error" key. Otherwise, the
fail ones will get called instead, by the virtue of Deferred object (here,
The last important advantage of promises I wanted to discuss here is their composability. What would be quite convoluted with vanilla callbacks, it’s very easy with promises. We can tie few
Promise objects together, creating a compound one that represents the joint state of all of them. This way it’s possible to, for example, launch a few AJAX requests concurrently and wait for them all to finish.
In jQuery, this can be achieved through the use of
$.when function. Simply pass a few existing
Promises to it and you will get a single one that encompasses them all:
Promise behaves in rather predictable way:
done callbacks are invoked when all “inner” promises are resolved, while
fail gets called when any of them is rejected. For more fine grained control, you can also attach a
then callback; there you’ll be able to evaluate results of each individual promise.
Okay, but what if we don’t want to hardcode all these
These examples should cover most of the use cases of promise objects. You may still want to use plain callbacks for the simplest of problems, but I’d recommend to at least have a try at doing it the, ahem, proper way :)
How about this?
Any resemblance to existing languages is purely coincidental.
Nice article ;)
This is a great promise object library https://github.com/kriskowal/q :)
@up: Looks good. As I understand, though, you may still need to use jQuery for AJAX if you don’t want to wrap XMLHTTPRequest yourself (and you probably don’t). For sharing code between client side and Node.js backend, Q looks quite superb, however.
@Tymur: Cute :) I think you could actually achieve something similar in JS if you add Underscore to the mix:
But personally, I’ll just… wouldn’t do that :)