The last column introduced some of Dojo’s AJAX machinery where we fetched content from a server using the dojo.xhrGet function. One thing that wasn’t mentioned, however, is that there is an underlying abstraction called a Deferred at play. Since the entire Dojo I/O subsystem uses this abstraction, it’s important to understand exactly what it buys you.

Dojo’s Deferred implementation is an adaptation of MochiKit’s excellent work, which is in turn inspired from Twisted. The beauty of a Deferred is that it allows network requests to be treated uniformly. It doesn’t matter if they’re synchronous or asynchronous, and you don’t need to have the foggiest idea of whether the request is still in flight, if it outright failed, or if it completed successfully ten minutes ago. Regardless of the various possibilities, you chain callbacks and errbacks onto the Deferred the same way, and it works as if you had set it up that way from the very beginning.

To illustrate how easy it is to leverage Deferreds in your own work, consider the following adaptation to the snippet of code from the previous column; updates are in bold.


// save the Deferred that is returned from the call
var d = dojo.xhrGet({
    url : "localresource.json",
    handleAs : "json",
    load : function(response, ioArgs) {
        /* Handle a successful callback here */

      return response;
    },
    error : function(response, ioArgs) {
        /* Handle any errors that occur here */

      return response;
    }
});

// arbitrarily add callbacks and errbacks to the Deferred...
// we have no idea about its exact execution state right now


d.addCallback(function(response) {
    /* Do something after the initial load callback finishes... */
    return response;
});

d.addCallback(function(response) {
    /* ...and then do something else. */
    return response;
});

d.addErrback(function(response) {
    /* But if there's an error, do this after the initial error handler finishes */
    return response;
});

/* You get the idea... */


It’s a given that the ultimate outcome of the dojo.xhrGet call will be that either the load or the error handler will fire, but the really slick part is that regardless of that outcome, the additional callbacks and errbacks that are added work just the same. Even if the dojo.xhrGet request had been a synchronous request and completed by the time the first callback was added, that callback would have fired right away. Better yet, you wouldn’t have needed to do any special checking or bookkeeping to make that happen. Are you starting to see the simplicity and elegance of a Deferred?

Be warned ahead of time that not returning the response from callbacks and errbacks is an incredibly common mistake; if the response is not returned, the expected propagation of the chained on calls may not occur since each callback or errback may depend on that response for the general case. It’s a great habit to always include a return response statement at the end of every load, error, addCallback, and addErrback so that you never get tripped up on this issue.

You can read more about Deferreds and Dojo’s I/O subsystem in my upcoming book, Dojo: The Definitive Guide.

As always, leave your thoughts, questions, or suggestions for future columns below — and thanks for reading!