jQuery Deferred Objects

November 18, 2011 3 Comments by Robin

If you’re a web developer, you may be asking yourself, ‘what are these “deferred objects” I keep hearing about?’ Hopefully, this article will help explain that.

Given the asynchronous nature of the web, and specifically JavaScript, a general asynchronous callback pattern has proliferated. So you have something you need to defer a bit so you use a timeout or an interval and you’re good to go…what’s the issue you may be asking? Well…web applications have become more and more complex, and what was once a nested callback or two, is turning into a hairy beast of nested callbacks.

The Deferred pattern addresses this nested nightmare by using using an idiom much like publish/subscribe (Observer) or Notifications (if you come from the land of iOS development). However, notification is a bit different because the caller has access to the Deferred object itself and can determine whether to call success or fail callbacks. This is sort of a forward reference, but will hopefully be clearer further down in this article. That said, let’s dig in…

If you’re familiar with stacks and queues then the underlying concept should be pretty straight forward. Essentially, the Deferred object implementation keeps a stack of callback function objects (similar to subscribers) for success or failure. Conventionally, these are called done callbacks or fail callbacks. Specifically, these function objects get pushed on the Deferred object when you call .then(myfunc), .done(myfunc), or .fail(myfunc):

var deferred = new Deferred();
deferred.done(function(message) {
  alert(message);
});
setTimeout(3000, function() {
  deferred.resolve("Hey buddy!");
});

First we create a deferred object. Immediately, the deferred object goes in to the “Pending” state.

Next, and here’s the important part to “grok”, by calling .done(… on the Deferred object, we are essentially asking it to add our function object to its array of callback functions.

Ok, so now we have 1..* functions queued (or stacked depending on implementation details) on the deferred object. Now one of two things will happen (at some point):
1. We call deferred.resolve (or one of it’s variants)
2. We call deferred.reject

If we do 1., this will “trigger” the deferred object to call all of the queued resolve category functions. These are the .done, .then, variant callbacks.

If we do 2., this will “trigger” the deferred object call all of the queued fail variant functions.

There are some semantic details to all of this, but that drives to the essence of the pattern. If you’re still not clear, I’ve drawn this extremely ugly diagram to help to visualize the essence of the deferred pattern (as described in both the jQuery API docs and CommonJS Promises page). Open your browser wide and start on the left and work to the right:

Deferred Object Pattern

Cool! So how is this Deferred object doing it’s magic internally? Here’s a simplified example of how it may be implemented:

 
<h1>JavaScript Deferred Objects Example</h1>
 
function Deferred() {
    var callbacks = [];
    this.done =  function(cb) {
        callbacks.push(cb);
    }
 
    this.resolve = function(arg) {
        for(var i = 0; i < callbacks.length; i++) {
            callbacks[i](arg);
        }
    }
}	
 
// Now we use the deferred object
var deferred = new Deferred();
deferred.done(function(message) {
  alert(message);
});
setTimeout(function() {
  deferred.resolve(&quot;Hey buddy!&quot;);
}, 3000);

What’s interesting about this pattern is that the owner of the deferred object determines whether we go in to the resolved or rejected state not the deferred object itself. This is slightly different from how a traditional Pub/Sub pattern would work, where the Publishing object itself would likely determine who/when to callback it’s subscribers. Because of this, it’s common to see usage where an intermediary function that is supplying some service (e.g. fetchResource, hitDatabase, etc.), will negotiate the initialization of the deferred object, determine whether to call resolve or reject, etc., and simply return the deferred object. So it looks like:

Deferred Pattern using a Broker

The above diagram only shows part of the picture. Once the Broker object calls resolve or reject, the original caller’s respective .done, .fail, etc., will get called.

All of this is possible because of JavaScript’s ability to treat functions as first class citizens (a fancy way of saying you can pass functions around and call them later and treat them like good ol’ objects!).

I hope this posting was somewhat helpful and revealed the magic behind Deferred Objects.

EDIT

My good friend Richard Bateman offers a point of clarification. There is actually a difference between .then and .done; .then allows you to provide two callbacks, the first for done and the second for fail.

Next part

For more on jquery deferred objects, see part 2 of this series.