We chose to focus on jQuery deferreds because jQuery is ubiquitous and because two important and familiar aspects of jQuery ($.ajax and animations) already have deferred support built in.
However, jQuery deferreds are certainly not the last word on the subject. As you will see, they differ markedly in one important respect from the many (at least 35) other deferred packages for JavaScript: jQuery deferreds do not currently follow the excellent Promises/A+ specification (see Promises/A+).
This book was written for the 1.10.2 or 2.0.3 versions of jQuery. Because the jQuery deferred API has changed several times, it is important to know where you stand, as we’ll see in Changes in the jQuery Deferred API. Server side, we’re using version 1.9.1 of the jQuery-deferred node module. The node module and much of the jQuery deferred code was written by Julian Aubourg.
Once you’re comfortable receiving promises from functions and working with them by attaching callbacks, you’ll soon want to write your own function or API that creates a deferred and returns its promise.
Construction
To create a deferred, you just call $.Deferred():
var deferred = $.Deferred();
As discussed in Terminology: Deferreds and Promises, instead of returning a deferred to the caller of your code, you’ll almost always want to return a promise. The promise is obtained by calling the promise method on the deferred:
var deferred = $.Deferred(),
promise = deferred.promise();
For convenience, the $.Deferred constructor allows you to pass it a function that can be used to initialize the deferred once it is created. The function receives the deferred as its only argument. For example, here’s a handy succeed function[6] that returns a promise that has already been fired with a specified result:
To make a deferred method for set, we don’t really need to know what items represents. All we need to know is that callback will be called by Chrome when it has set the key values it is given. Our version of set will look as follows:
Create a promise variable that we’ll eventually return. The deferred from which the promise is made is resolved immediately with an undefined value. When we first call then on that promise, the function we pass to then will be run immediately. Note that we don’t care about the value the promise fires with; we’re just trying to get the task functions run sequentially.
2
makeRunner functions as a closure that returns a function to invoke the current task with its arguments. Note that we assume that calling func returns either a promise or something that can be used to obtain a promise.
There is an unfortunate subtlety here. All promises have a promise method (undocumented!) that, when called with no arguments, returns the promise. The method is actually identical to the (documented) promise method of its deferred. So if func returns a promise, calling promise() on it simply returns the promise. More usefully, if func happens to be a call to animate, the call to promise() is essential—it ensures that the returned value, passed to then, is a promise. See the challenges below for more on this.
3
This slightly dubious line reuses (updates) the promise variable to hold the promise returned by calling then on the current promise. In effect, we’re adding a new function to be called when the current promise is done and then forgetting about that promise. We only have to worry about the most recent promise, which we return to our caller. Because the original promise was fired on creation, we don’t have to hang onto it for any reason.