dojo.Deferred is very cool, but it’s very easy to tie yourself up in knots. It’s worth spending some time reading about it. The official API doc is good, as is this article which discuss what’s new in Dojo 1.5 on the topic of Deferreds.
One thing in particular that isn’t always obvious is that a callback function itself can return a Deferred. In this case, Dojo will do the right thing, and insert this into the callback chain in the right place. This can make your code much simpler and avoid nasty hacks with co-dependent Deferreds. To illustrate, let’s start off with a simple example that doesn’t exploit that feature:
function sayHello() {
var deferred = new dojo.Deferred();
var name = "Fred";
console.debug("Hello", name);
deferred.callback(name);
return deferred;
}
sayHello().then(function(name) {
console.debug("Goodbye", name);
});
This should print “Hello Fred”, then “Goodbye Fred” in your debugger. Note that we are firing the callback explicitly above. In reality, this is far more likely to be based on the result of an asynchronous call – typically an XHR request. Treat the sayHello() method as a black-box for the purposes of this illustration – the important point is that it returns a Deferred (and that aspect of the function signature can’t be changed).
Now let’s imagine that you need to insert sayHello() into the middle of a Deferred chain. Let’s remove the final three lines from the code above, and add this additional code:
function prepareForArrival() {
var deferred = new dojo.Deferred();
console.debug("Preparing for the arrival of an unknown guest");
deferred.callback();
return deferred;
}
prepareForArrival().then(sayHello).then(function (name) {
console.debug("Goodbye", name);
});
You should now see: “Preparing for the arrival of an unknown guest”, then “Hello Fred”, then “Goodbye Fred”.
This works because the then() method takes a callback function as its first argument. Often, you will specify a function which returns a regular value to be passed along to the next callback (i.e. the next then() method) in the chain. However, in this case, we have specified a function which returns a Deferred. Dojo realises this, and inserts that Deferred into the call chain instead. It is executed next, and the result of the callback method from that Deferred is passed into the “goodbye function”.
Remembering this trick can greatly simplify the readability and structure of your code, and allow you to construct complex Deferred chains in a straightforward manner.
That’s a REALLY nice and smart feature INDEED. You can add some dependencies between Deferred like one Deffered cannot start until the other get the result… Brilliant!