Daniel Taveras

How to Use jQuery Deferred Objects

Code that runs in the browser needs to be quick. Certain procedures happen asynchronously in order to avoid dragging down the browser, and it is easier to mange this all with Promises.

While jQuery Deferred objects do not follow the Promises/A+ spec, they serve as an excellent first foray into a world with better async code.

It is entirely possible that your async code is peppered with callbacks, this can spiral into what we call Callback Hell. Deferreds can be used in virtually every scenario where you currently have a callback function.

Every Deferred individual has a state, ‘pending’, ‘rejected’, or ‘resolved.’ This can be accessed using its state method. All Deferreds begin in the ‘pending’ state, and will only switch to ‘rejected’ when we call the reject method, or ‘resolved’ when we call the resolve method. Pretty simple, right?

Let’s look at an example:

// Creating the Deferred
var dfd = $.Deferred();

// Attaching the callbacks
dfd.done(function() {
alert("We are resolved!");
});

dfd.fail(function() {
alert("We are rejected!");
});


// Changing the Deferred's state.

dfd.resolve();
// try dfd.reject(); instead!

We can see here that we create a Deferred using the jQuery.Deferred method. Next, we attach callbacks using our new Deferred object’s done and fail methods. These are triggered when the state of our Deferred changes. These are the most common callbacks, but there are a few others as well. Afterwards, we change the Deferred’s state to ‘resolved’ by calling its resolve method.

Let us look at an actual async example:

// Give us a random 'true' or 'false'
function randomBoolean() {
return Math.random() < 0.5;
}
function tryToConnect() {
var dfd = $.Deferred();

window.setTimeout(function() {
if( randomBoolean() ) {
dfd.resolve();
}
else {
dfd.reject();
}
}, 1000);

return dfd.promise();
}

Our function tryToConnect handles all of the implementation details for our async operation. We idle for a second, and will randomly resolve or reject our Deferred object. Our tryToConnect function returns the result of our Deferred’s promise method. This ‘promise’ object, not to be confused with the real Promises spec, lets us provide a nice interface for our async procedure.

Code that references this object can attach callbacks, but is unable to change the Deferred’s state. If we returned the Deferred itself as opposed to the result of the promise method, users of our tryToConnect function would be able to resolve a Deferred themselves! Lets see how we can use this function we have created:

tryToConnect()
.done(function() {
alert("Successful!");
})
.fail(function() {
alert("Unsuccessful!");
})
.always(function() {
alert("Attempt finished!");
});

We take the promise object returned by tryToConnect and attach three callbacks to it. Here we are also making use of the ‘always’ callback, which will be triggered once a Deferred leaves the ‘pending’ state.

This is all pretty cool, but what if we need to take some steps after a few operations have all completed? That is when the nifty jQuery.when method comes to the rescue! Here is how we can use it:

var first  = tryToConnect();
var second = tryToConnect();
var third = tryToConnect();
var fourth = tryToConnect();

$.when( first, second, third, fourth )
.done(function() {
alert("All successful!");
})
.fail(function() {
alert("Errors occurred!");
})
.always(function() {
alert("All attempts complete!");
});

This method takes an unlimited amount of Deferreds are arguments. Any arguments that are not Deferred objects, are assumed to be resolved. You can treat the returned object from jQuery.when as a Deferred itself! In the above example, we will only get alerts once all four Deferreds have completed.

This method lets us have real freedom with async code, here is a final bit of code to poke at:

var dfdList = [];
var total = Math.floor(Math.random() * 20);

for(var i = 0; i <= total; i++) {
dfdList.push( tryToConnect() );
}

$.when.apply( jQuery, dfdList )
.always(function() {
alert( "All " + dfdList.length + " attempts completed!")
});

Look at that! We have an array with a random amount of Deferreds in it. Using the apply method that each JavaScript function object has, we can pass in this array as the list of arguments that jQuery.when needs. Using this method gives us flexibility when dealing with a large and unknown number of asynchronous procedures.

But to surmise, after living a large amount of your JavaScript life in callback hell, Deferreds are a breath of fresh air. While they are abstract, they are still super easy to wrap your mind around. And of course, you can eventually graduate to regular Promises!