Wednesday 7 March 2012

jQuery Deffered Object.


Using Deferred objects in jQuery 1.5+

Deferred objects are a new addition to jQuery 1.5, and are meant to strike a cleaner division between executing a task and waiting for the task to complete (and reacting accordingly). In this article, we’ll talk about what deferred objects are and what they can do for you, and finish everything off with a simple example application.


Using Deferred objects in jQuery 1.5+

Deferred objects are a new addition to jQuery 1.5, and are meant to strike a cleaner division between executing a task and waiting for the task to complete (and reacting accordingly). In this article, we’ll talk about what deferred objects are and what they can do for you, and finish everything off with a simple example application.

If you’ve worked with Javascript a fair deal, then it’s probable that you’ve run across callback functions before. These are most prevalent in asynchronous operations (AJAX comes to mind, of course), and basically provide a way to execute something when an asynchronous operation completes (e.g. react accordingly when an AJAX operation succeeds or fails).
Let’s take a vanilla pre-jQuery 1.5 AJAX request as an example:



Now, with the advent of deferred objects in jQuery 1.5, all $.ajax() derivatives now return deferred objects, which allow you to do the following:


“So what?”, you may say. Well, if you haven’t noticed, the syntax on registering function callbacks is now more akin to the standard way of registering handlers in jQuery, such as with .click() or even .bind() and .delegate(). This allows us to chain registrations into the more familiar way of doing it in jQuery.


The less-noticed advantage of this syntax (and, in my opinion, it’s greatest boon) is how it enables us to easily slap on multiple handlers onto the task’s result events, just like with the vanilla jQuery events such as .click(). The example above shows just that: we registered two separate function literals as two separate handlers for the success callback of a single AJAX operation.


 The old JSON parameter syntax of registering handlers on AJAX calls only allowed us to slap on one callback per event per call. Of course, we could go and create a function literal that calls all the necessary function handlers, but as systems scale and grow more complex, it gets harder and harder to maintain and keep track of everything that’s expected to happen when something happens.


Taking stuff one notch higher

While deferred objects make it so much easier to attach multiple event handlers on a single asynchronous call, that’s easily eclipsed by the fact that deferred objects also allow you to do the exact opposite: performing a task once several tasks are completed.
Let’s tackle this with a more real-world example. Imagine that we had a web page that makes two AJAX calls: one to get data from the server, and another to get an HTML template markup snippet. What we aim to do is that when the two AJAX calls resolve, we want to get our HTML template, slap the fetched content somewhere in it, and append the result into the current page’s DOM.


The code above definitely has a number of improvement points going for it, but it effectively illustrates how we may go about with doing the business case we raised. While the code is easily readable and effectively clear, it suffers from the same problems that scalable applications aim to eliminate: as the code expands and the system grows more complex, we’re looking at maintaining larger and larger blocks similar to that above, and eventually that will just be too difficult to maintain and someone will likely trip up.
Along with deferred objects, jQuery also exposes the new $.when() utility function that takes care of managing tasks similar to the one above.

$.when() accepts any number of deferred objects (which $.ajax() derivatives now return), and automatically attach event handlers to the tasks as a singular collective. The resulting data from each deferred object are mapped to the handler function’s parameters in the same order as they were declared (i.e. the resulting data from $.ajax({ url : 'ajax.svc/getdata' }) is mapped to data in the function literal in the .then() call, and so on).
This construct accepts three primary handlers:
  • .done() – called when all deferred tasks succeed
  • .fail() – called when at least one of the deferred tasks fail
  • .complete() – called as soon as all deferred tasks complete, whether or not they succeeded or not
The .then() is shorthand for declaring both .done() and .fail() : the first parameter takes the on-success callback, while the second takes the on-fail callback.

Creating a deferred object

While $.ajax() derivatives now automatically return a deferred object, you may want to manually create deferred objects in your code for specific purposes.
To create a deferred object, you simply call $.Deferred(). These objects basically have three states: unresolved, resolved and rejected.
Deferred objects start off as unresolved, and are either resolved or rejected based on a task’s outcome. When manually handling deferred objects, you’d want to either call .resolve() or .reject() based on a task’s outcome.

Let’s build our own deferred logic example

To put everything we’ve discussed together, we’ll try to create a very simple system that makes use of deferreds.
We’ll use an application that has three INPUT fields. The user is expected to fill up all three fields, so we want a task to run when all three fields have been completed. For this example, we’re going to use deferred objects to update a simple message when all three fields have been filled up by the user.
To start off, here is our completed application:



Let’s get started!
We’ve got three INPUT fields that we want filled, so it makes sense to create three separate deferred objects; one for each field.


We then initialize the task to perform once all our deferred objects resolve. In this case, we just want to change the text in a simple SPAN element.


 Finally, we need to manually resolve our deferred objects at the proper time. In our example, we want to resolve the corresponding deferred object when the value of an INPUT field is changed.


we’re done. Our complete code is below:


 Our example can use some improvements (like caching $('#example-pane') for example), but hopefully it has been a clear illustration of how jQuery deferred objects can work with you in your code.

===================================================
(Other explanation of Deffered Object).
Deffered Object.

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.' );
   });



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
    });

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
    });

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.