erichynds

Hi, I'm Eric Hynds, a front-end website developer living outside of Boston, Massachusetts. I'm passionate about developing functional, standard-compliant, and user-friendly websites.

Using Deferreds in jQuery 1.5

Deferreds, new in jQuery 1.5, decouple logic dependent on the outcome of a task from the task itself. They’re nothing new to the JavaScript scene; Mochikit and Dojo have implemented them for some time, but with Julian Aubourg’s AJAX rewrite landing in 1.5, deferreds in jQuery was the logical next step. With deferreds, multiple callbacks can be bound to a task’s outcome, and any of these callbacks can be bound even after the task is complete. The task in question may be asynchronous, but not necessarily.

What’s more, deferreds are now built-into $.ajax() so you’ll get them automatically. Handlers can now be bound like this:

// $.get, an ajax request, is asynchronous by default.
var req = $.get('foo.htm')
   .success(function( response ){
      // do something with the response
   })
   .error(function(){
      // do something if the request failed
   });
 
// this might execute before the $.get() above is complete
doSomethingAwesome();
 
// something additional to execute upon success, which might, or might not,
// have fired by now.  With $.ajax deferreds built-in, it doesn't matter.
req.success(function( response ){
   // do something more with the response
   // this will fire when success normally fires, or fire immediately
   // if prior success callbacks have already fired
});

We are no longer limited to one success, error, or complete handler anymore, and instead of simple callback functions, these hooks are now self-managed first-in, first-out callback queues.

As shown in the example above, callbacks may be attached even after the AJAX request – or any observable task – has completed. For code organization this is great; the days of long unwieldy callbacks may be over. It’s almost as if $.queue() meets pub/sub.

Digging a little deeper here, imagine a scenario where we want to call a function after several concurrent AJAX requests have completed. This is easily accomplished with $.when(), deferred’s little helper method:

function doAjax(){
   return $.get('foo.htm');
}
 
function doMoreAjax(){
   return $.get('bar.htm');
}
 
$.when( doAjax(), doMoreAjax() )
   .then(function(){
      console.log( 'I fire once BOTH ajax requests have completed!' );
   })
   .fail(function(){
      console.log( 'I fire if one or more requests failed.' );
   });

View this example on jsFiddle

The reason this works is because all of jQuery’s AJAX methods now return an object containing a "promise", which is used to track the asynchronous request. The promise is a read-only view into the result of the task. Deferreds look for the presence of a promise() method to determine whether an object is observable or not. The $.when() waits for all its AJAX requests to execute, and once they do, the callbacks attached to the $.when() via .then() and .fail() will fire as appropriate (depending on task’s success or failure state). The callbacks fire in the order they were assigned.

It gets better: all deferred’s methods accept either functions or arrays of functions, so you can build your behaviors and assign them all with one call, or in separate calls, as you please.

$.ajax() returns an object packed with other deferred-related methods. I discussed promise(), but you’ll also find then(), success(), error(), and a host of others. You don’t have access to the complete deferred object, though; only the promise, callback-binding methods, and the isRejected() and isResolved() methods, which can be used to check the state of the deferred.

But why not return the whole object? If this were the case, it would be possible to muck with the works, maybe pragmatically "resolve" the deferred, causing all bound callbacks to fire before the AJAX request had a chance to complete. Therefore, to avoid potentially breaking the whole paradigm, only return the dfd.promise().

Registering Callbacks

In the examples thus far I’ve used the then(), success(), and fail() methods to register callbacks onto the deferred, but there are more methods available to you, especially when working with AJAX deferreds. The method you choose ultimately depends on the resolution state(s) you’d like to bind to.

Available to all deferreds (AJAX, $.when, and those created manually):

.then( doneCallbacks, failedCallbacks )
.done( doneCallbacks )
.fail( failCallbacks )

AJAX deferreds have three additional methods, two of which map to one of the above. They are provided as semantic alternatives and match the names of the "old" handlers we’re all used to:

// "success" and "error" map to "done" and "fail" respectively.
.success( doneCallbacks )
.error( failCallbacks )

You can register a complete handler that’ll fire regardless of the success or failure state of the request. Unlike success and error, complete is actually an alias to the done method of a separate deferred. This separate deferred, created internally by $.ajax(), is resolved after an AJAX request completes, regardless of the outcome.

.complete( completeCallbacks )

Therefore, the following three examples are equivalent (success reads better than done in the context of an AJAX request, don’t you think?)

$.get("/foo/").done( fn );
 
// same as:
 
$.get("/foo/").success( fn );
 
// same as:
 
$.get("/foo/", fn );

Creating your own Deferred

We know that $.ajax and $.when implement the deferred API internally, but you can also create your own implementations:

function getData(){
   return $.get('/foo/');
}
 
function showDiv(){
    var dfd = $.Deferred();
 
    $('#foo').fadeIn( 1000, dfd.resolve );
 
    return dfd.promise();
}
 
$.when( getData(), showDiv() )
    .then(function( ajaxResult ){
        console.log('The animation AND the AJAX request are both done!');
 
        // 'ajaxResult' is the server's response
    });

View this example on jsFiddle

Inside showDiv() I’m creating a new deferred object, performing an animation, and returning the promise. The deferred is resolved (think of dequeue() if you’re familiar with jQuery’s queuing methods) after the fadeIn() call completes. Between the time the promise is returned and the deferred is resolved, a then() callback is registered to the successful completion of both asynchronous tasks. Therefore, once both tasks resolve, the callback is fired.

getData() returns an object with a promise method, which allows $.when() to observe its eventual resolution. The manually steps we took to return a promise in showDiv() is handled for us internally by $.ajax() and $.when().

1/15/2011: As Julian pointed out in the comments, the above syntax can be shortened using the $.Deferred(fn).promise() signature. The following two approaches to creating a deferred are equivalent:
function showDiv(){
    var dfd = $.Deferred();
 
    $('#foo').fadeIn( 1000, dfd.resolve );
 
    return dfd.promise();
}
 
// same as:
 
function showDiv(){
    return $.Deferred(function( dfd ){
        $('#foo').fadeIn( 1000, dfd.resolve );
    }).promise();
}

Defer your Deferreds

We could take this one step further by registering individual callbacks to both getData() and showDiv(), as well as registering their individual promises onto one "master" deferred.

If you wanted something to happen on the success of getData() and on the success of showDiv() (independently of the other), as well as on the success of both getData() and showDiv() combined, simply register a callback to their individual deferreds, and tie them together with $.when:

function getData(){
   return $.get('/foo/').success(function(){
      console.log('Fires after the AJAX request succeeds');
   });
}
 
function showDiv(){
    return $.Deferred(function( dfd ){
        $('#foo').fadeIn( 1000, dfd.resolve );
    }).promise();
}
 
$.when( getData(), showDiv() )
    .then(function( ajaxResult ){
        console.log('Fires after BOTH showDiv() AND the AJAX request succeed!');
 
        // 'ajaxResult' is the server’s response
    });

View this example on jsFiddle

Chaining Hotness

Deferred callbacks can be chained so as long as a promise is returned from the function. Here’s a real world example (via @ajpiano!)

function saveContact( row ){
   var form = $.tmpl(templates["contact-form"]),
      valid = true,
      messages = [],
      dfd = $.Deferred();
 
   /*
      bunch of client-side validation here
   */
 
   if( !valid ){
      dfd.resolve({
         success: false,
         errors: messages
      });
   } else {
      form.ajaxSubmit({
         dataType: "json",
         success: dfd.resolve,
         error: dfd.reject
      });
   }
 
   return dfd.promise();
};
 
saveContact( row )
   .then(function(response){
      if( response.success ){
         // saving worked; rejoice
      } else {
         // client-side validation failed
         // output the contents of response.errors
      }
   })
   .fail(function(err) {
      // AJAX request failed
   });

The saveContact() function first validates the form and saves the result into the variable valid. If validation fails, the deferred is resolved with an object containing a success boolean and an array of error messages. If the form passes validation, the deferred is resolved, except this time the success handler receives the response from the AJAX request. The fail() handler responds to 404, 500, and other HTTP errors that could prevent the AJAX request from succeeding.

Non-observable Tasks

Deferreds are particularly useful when the logic to execute may or may not be asynchronous, and you want to abstract that condition out of the mainline code. Your task might return a promise, but it might also return a string, object, or some other type.

In this example, the first time a "launch application" link is clicked on, an AJAX request tells the server to record (and return) the current timestamp. The timestamp is stored in the element’s data cache after the AJAX request is complete. The application only cares about the initial first click though, so on subsequent clicks, the timestamp is read out of the data cache instead of making an additional trip to the server.

function startTask( element ){
   var timestamp = $.data( element, 'timestamp' );
 
   if( timestamp ){
       return timestamp;
   } else {
       return $.get('/start-task/').success(function( timestamp ){
           $.data( element, 'timestamp', timestamp );
       });
   }
}
 
$('#launchApplication').bind('click', function( event ){
   event.preventDefault();
 
   $.when( startTask(this) ).done(function( timestamp ){
       $('#status').html( '<p>You first started this task on: ' + timestamp + '</p>');
   });
 
   loadApplication();
});

When $.when() recognizes that its first argument doesn’t have a promise (and therefore is not observable), it creates a new deferred object, resolves it with the data, and returns the promise from the deferred. As such, something arbitrary without an initial promise can be observed.

One small gotcha to be aware of (which will most likely be addressed in the next maintenance release), is that you cannot defer an object that implements it’s own promise method. Deferreds are detected by the presence of a promise method, but jQuery doesn’t check to see if the promise actually returns a usable object. Therefore, this will throw a syntax error:

var obj = {
   promise: function(){
      // do something
   }
};
 
$.when( obj ).then( fn );

Conclusion

Deferreds introduce a new, robust approach to writing asynchronous tasks. Instead of focusing on how to organize callback logic into a singular callback, you can assign several individual actions to callback queues knowing that these will be executed, in context, without worrying so much about synchronicity. The info here might be a lot to digest, but once you get the hang of it, I think you’ll find writing asynchronous code to be much easier with deferreds.

Many thanks to Steven Black for editing this post, as well as the #jquery-dev crew for reinforcing my understanding of deferreds. Adam hooked me up with a sweet example, too.

Related posts:

  1. A Recursive setTimeout Pattern

Tags: , ,

  • http://cuppster.com Jason

    Thanks for the great tutorial!

  • http://cuppster.com Jason

    Thanks for the great tutorial!

  • http://cuppster.com Jason

    Thanks for the great tutorial!

  • http://bradshawenterprises.com Richard Bradshaw

    Really like the non-observable task demo – that’s the first time I’ve seen something that I will use frequently!

  • Anonymous

    ..

  • http://twitter.com/chrisjm Chris Mears

    Awesome post. Thank you!

  • Marcus Ekwall

    Thanks for enlightening us on deferreds. You’re the best!

  • Guillaume Voisin

    Nice! This brings ajax requests implementation a lot further and much more easier !

  • Julian Aubourg

    Just a couple of precisions:

    complete is not an alias for then seeing as a complete callback will be called whether the ajax internal deferred ends up resolved or rejected (and that for backward compatibility reasons). The signature for complete is thus .complete( completeCallbacks ) and has no equivalent in deferreds.

    You can shorten a lot of your examples by using the jQuery.Deferred( fn ) signature which exists specifically to allow inlined deferred creation into jQuery.when:

    For instance:
    $.when( $.Deferred(function( dfd ) {
    $(‘#foo’).fadeIn( 1000, dfd.resolve );
    }) )
    .then(function( ajaxResult ){
    console.log(‘The animation AND the AJAX request are both done!’);

    // ‘ajaxResult’ is the server’s response
    });

    is equivalent to:

    function showDiv(){
    var dfd = $.Deferred();

    $(‘#foo’).fadeIn( 1000, dfd.resolve );

    return dfd.promise();
    }

    $.when( showDiv() )
    .then(function( ajaxResult ){
    console.log(‘The animation AND the AJAX request are both done!’);

    // ‘ajaxResult’ is the server’s response
    });

    Very nice article, Eric. It’s nice to see this kind of deep dive into Deferreds.

  • Anonymous

    Thanks Julian! I updated the callback section accordingly. Great tip about the jQuery.Deferred( fn ) signature; I had no idea this existed. Well done, sir!

  • http://thedutchpirates.com Sipke Schoorstra

    Thanks for this excellent post! This clarifies a lot for me about Deferreds (when I first read about them yesterday I didn;t even realize the benefits and thought it would be something ‘d never use) Especially the Non-observable Task demo is something I will be using over and over in my current project, making the code even more beautiful :)

  • Jusitn

    Is it possible generate a list of functions on the fly (say based off of items in an array) and pass them to a single $.when()?

  • Anonymous

    Justin, take a look at this fiddle: http://jsfiddle.net/ehynds/MU3Rq/5/

    $.when doesn’t take an array of functions but it will take any number of arguments; therefore, you can .apply an array of function promises to $.when.

  • http://twitter.com/wbotelhos Washington Botelho

    Very useful this functionality.
    It will decrease the giant chains of callbacks.

    Great post!

  • fschaefer

    Beside the fact that this is amazing stuff, you can even abuse the syntax for just grouping source code:

    /* Init Dialog */
    $.when (new function () {
    console.log (‘Init Dialog’);
    })
    /* Init Tab I */
    .then (function () {
    console.log (‘Init Tab I’);
    })
    /* Init Tab II */
    .then (function () {
    console.log (‘Init Tab II’);
    })
    /* Show Dialog */
    .done (function () {
    console.log (‘Show Dialog’);
    });

  • http://twitter.com/jancel Jeff Ancel

    Thanks for the good demos and wonderful writeup. I’m sure this has been a long time coming and is celebrated by myself and others.

  • http://twitter.com/jaubourg j@ubourg

    I think it’s more fun with some randomness added into the mix :)

    http://jsfiddle.net/Ls2kd/3/

  • mmikeyy

    Excellent post. It’s a shame that the official documentation leaves so much to be desired.

  • http://twitter.com/quickredfox quickredfox

    Could you explain why you return dfd.promise() instead of simply dfd ? I find both work, wondering which is better and why.

  • http://www.bennadel.com Ben Nadel

    Eric, really great post. I was definitely very confused on the Deferred stuff and this helped clarify a lot of it. I also really like Adam’s AJAX example. Definitely gave me a lot to think about.

  • Anonymous

    Returning the promise makes the deferred “read only” in a sense. If the deferred were returned it would be possible to muck with the works, maybe pragmatically resolve the deferred (causing all bound callbacks to fire) before the async code has had a chance to complete.

    You can still bind handlers to a promise, just not resolve it.

  • http://jandreas.myopenid.com/ Andreas Goebel

    Very nice article Eric. I Like the fact to have a promise-maker buildin to jQuery, but I didn’t really find a reasonable spot to use them so far. Changed like a lot now :-)

  • Jason Capriotti

    Stupid question:
    Shouldn’t the call to dfd.resolve actually be dfd.resolved() ? Otherwise you are not actually calling a method…

  • http://www.bennadel.com Ben Nadel

    @Jason,

    You pass the method without the () because you are not calling it at that time. Rather, you’re passing the method reference to another context which takes care of invoking it at a later time. Essentially, you’re treating “resolve” itself as a callback.

  • Mylz

    Unless I’m misunderstanding this, shouldn’t :

    $.get(“/foo/”).done( fn );
    // same as:
    $.get(“/foo/”).success( fn );
    // same as:
    $.get(“/foo/”, fn );

    Really read:

    $.get(“/foo/”).done( fn );
    // same as:
    $.get(“/foo/”).complete( fn );
    // same as:
    $.get(“/foo/”, fn );

  • passive

    Excellent, I was just trying to figure this one out. :) I didn’t know about apply.

  • Carter Thaxton

    Just wanted to point out that there’s no need to call .promise() on the result of jQuery.Deferred( fn )

    In your updated post, you’ve got:
    return $.Deferred(function(dfd) { … }).promise();

    This can be shortened to:
    return $.Deferred(function(dfd) { … });

    Julian’s suggestion didn’t actually use it either, but then your update to your code example above still used it.

  • Anonymous

    Hi Carter,

    I suppose it’s safe to leave it out if the deferred is created in $.when, but just for consistency sake I put it in there as a good practice. If you were to do something like:

    var dfd = $.Deferred(function(){
      // something async
    });
    
    $.when( dfd ).then( fn );
    

    It becomes more important to return the promise, just to lock in that “read only” view of the deferred.

  • http://twitter.com/sprynmr Bob Spryn

    There are four things I don’t quite get here.

    In the section “Creating your own Deferred”:

    How does the ‘then’ callback get access to the ajaxResult of the first promise? What if there were multiple ajax requests instead of one custom deferred. Would then have access to each of them as separate arguments?

    In the section “Defer your Deferreds” you mention adding a callback to both getData and showDiv. I see the callback in getData (the console.log that happens after your task executes.) But I don’t see it in showDiv:

    function showDiv(){
    return $.Deferred(function( dfd ){
    $(‘#foo’).fadeIn( 1000, dfd.resolve );
    }).promise();
    }

    Isn’t the execution of the fade on #foo the task itself and not a callback?

    Second, in “Chaining Hotness”

    In the ‘then’ callback, are you relying on the ajax data response to contain a boolean called ‘success’? This would have to be explicitly added correct? Such that either your ‘failed validation’ case and your ajax response would have to define a property called success?

    saveContact( row )
    .then(function(response){
    if( response.success ){
    // saving worked; rejoice
    } else {
    // client-side validation failed
    // output the contents of response.errors
    }
    })
    .fail(function(err) {
    // AJAX request failed
    });

    In the “Non-observable Tasks” section I’m trying to follow the flow.

    Does the .success() callback return the ajax object with a promise to $.when the first time around? The timestamp argument that $.when’s callback is using, is that the straight ajax result the first time around?

  • Anonymous

    No, complete is a separate deferred that is resolved whether or not the ajax request succeeds. success and error are simply aliases to the done and fail methods on the deferred created specifically to handle the AJAX request.

  • Anonymous

    Yes I also don’t understand how the return parameters work.
    e.g. I would just like to create my own deferred object, that is the only one running in a when statement and I want it to return a value to my then callback

  • Anonymous

    Deferred is too new for me. I am searching if the new major changes in jQuery 1.5 Ajax have the way to Ajax Get queue in sequence without using ajaxqueue plugin since it too old. Based on my understanding, Ajax queue and Deferred does not related. Am I right?

  • http://twitter.com/ashishanexpert Ashish

    All gone over the mind…. :)

  • http://twitter.com/sprynmr Bob Spryn

    Thanks Clayton! That explanation (plus some time playing with them) really cleared up my understanding.

  • http://profiles.google.com/djether Michael Behan

    If you want something to fire when all requests are complete you can just wait for the condition “$.active === 0″

  • http://a-developer-life.blogspot.com/ Diego

     Hi Eric, great post! I am going to use Deferred a lot, I have written about them as well…specifically about how you can integrate them with Backbone http://a-developer-life.blogspot.com/2011/05/using-jquery-deferred-with-backbone.html

  • Britt O’Halloram

    this is a killer use case that’s poorly documented – thanks!

  • Nick Day

    This is possibly the most useful and clearly explained blog post I have ever read – thank you!

  • http://twitter.com/webspin R’phael Spindel

    While  .complete/.success/.error may ‘read better’ when done in the context of an Ajax request, the jQuery API docs do make it clear that this alias will be deprecated in jQuery 1.8 and so will eventually be removed, all new and existing code should be written strictly using  .done/.fail/.always calls.

  • http://twitter.com/webspin R’phael Spindel

    Actually, I would not consider this abuse at all, I think it is now imperative that one develop all their code, even if it is currently only synchronous, with an eye that eventually, almost all code will grow and wind up at some point using an asynchronous call.  If you initially start to structure all your projects like this, when it comes the time to include an asynchronous call, adding it will be a trivial matter.

  • http://twitter.com/webspin R’phael Spindel

    Returning the deferred as opposed to the read-only contract of the promise actually opens up some interesting use cases.  There can easily be situations where one wants to short-circuit the async queue programmatically (and pragmatically!)  by resolving the deferred before all callbacks completed, but it should be well-understood that this is desired and not a side-effect use.  

  • http://thomasburleson.myopenid.com/ ThomasBurleson

    The non-observable task demo and the explanation of $.Deferred( fn ) are VERY nice.
    And your follow-up with detailed responses to comments was very helpful.

    Loved the article and the samples.
    Thank you,
    ThomasB

  • http://limerick2.livejournal.com/ fred hixton

    Thanks for the fiddle link! Fred @ Dog Beds

  • http://pulse.yahoo.com/_AKIWO4UL6ICRN4PXJWFFMX2XEQ Tim

    Exactly my point but that’s just fine in the end. Tim @ wrestling shoes

  • http://pulse.yahoo.com/_AKIWO4UL6ICRN4PXJWFFMX2XEQ Tim

    Right quite so, a wonderful writeup. I’m still counting on the official spot factoring documentation to be honest.