Dojo DeferredList

By  on  

Deferreds are all the rage in the JavaScript community these days and who can argue?  Deferreds, an object representing an asynchronous action, make working with AJAX requests incredibly easy -- no callback hell, no problem passing request information around.  What if you want to work with multiple Deferreds though?   For example, say you want to execute functionality once multiple AJAX requests complete.  The Dojo Toolkit has you covered with dojo/DeferredList, a resource whose API mirrors a single Deferred object but is capable of handling multiple Deferreds.

Using Deferreds

Dojo returns Deferred objects from many operations, including animation and XHR requests.  With the Deferred object, you can use the then property to react to the response once the async interaction is completed:

// A very basic usage of dojo/request
request("service.php?id=someid").then(function(response) {
	console.log("request result is:", response);
});

The example above shows a basic AJAX request and handling of the resulting Deferred.  Easy, right?  But what if you want an action to execute only after multiple Deferreds have resolved?

Using dojo/DeferredList

The DeferredList is a manager for multiple Deferreds, making handling multiple Deferreds a breeze:

// Require the Dojo dependencies
require(["dojo/request", "dojo/DeferredList"], function(request, DeferredList) {
	console.log("Request loaded!", request);

	// Request 1
	var promise1 = request("/endpoint/1").then(function(response) {
		console.log("request 1 result", response);
	});

	// Request 2
	var promise2 = request("/endpoint/2").then(function(response) {
		console.log("request 2 result", response);
	});

	// Create a DeferredList to manage both of them
	var list = new DeferredList([promise1, promise2]);
	// When they're both resolved...
	list.then(function(result) {
		// result is:  [Array[2], Array[2]]
		// result item[0] is the result of each request

		// Do something!
		
	});
});

The example above represents all roses:  all successful Deferreds.  What if a Deferred fails;  a 404 error, request timeout, or a Deferred rejection?  A little validation cures all that:

// Require the Dojo dependencies
require(["dojo/request", "dojo/DeferredList"], function(request, DeferredList) {
	console.log("Request loaded!", request);

	// Request 1
	var promise1 = request("/endpoint/1").then(function(response) {
		console.log("request 1 result", response);
	});

	// Request 2
	var promise2 = request("/endpoint/2").then(function(response) {
		console.log("request 2 result", response);
	});

	// Request 3:  A request that will fail...
	var promise3 = request("/endpoint/noexist").then(function(response) {
		console.log("request 3 result (fail)", response);
	});

	// Create a DeferredList to manage both of them
	var list = new DeferredList([promise1, promise2, promise3]);
	// When they're both resolved...
	list.then(function(result) {
		if(request[0][0] && request[1][0] && request[2][0]) { // request[2][0] is false
			// Success!
		}
		else {
			// React to a failure
		}
	});
});

If the promise for a given Deferreds returns false, you know the request (or async action) failed.  The second argument returned by the Deferred provides information about the request and why it failed:

{
	"message": "Unable to load noexist.php status: 404",
	"response": {
		"url": "/endpoint/noexist",
		"options": {
			"headers":{}
		},
		"xhr": {
			"statusText": "Not Found",
			"status": 404,
			"response": "{404 page html}"}
		}
}

The example here is oversimplified.  One realistic example is the one I used to create the dojox/mobile TweetView example, sending multiple JSONP requests (Twitter profile and Twitter timeline list) for a client-side Twitter widget.  The dojo/DeferredList resource makes handling multiple asynchronous actions easy and enjoyable.  Just another reasons to use the Dojo Toolkit in your next web application!

Recent Features

Incredible Demos

Discussion

  1. Be sure to check out dojo/promise/all in 1.8:

    all([def1, def2]).then(function(results){
        // results[0], results[1]
    });
    
    • I like that DeferredList is backward-compatible and better documented :)

  2. Markus

    jQuery’s $.when() does the same job, but the api looks more intuitiv

  3. Cesar

    Is there any way to learn Dojo that isn’t a pain in the back? I’m trying and I have quite a bit of experience on other libraries, but Dojo does things in such a crazy way (or the docs are really bad at explaining things), for example, I still can’t understand why make such a huge change just to avoid global scope, after all, if you just use Dojo, you won’t have name conflicts (and even then you can just use dojo.blahblah).

    Sorry if this looks like I’m against Dojo, I’m not, in fact I’m trying to migrate from Mootools, but besides being “the more correct way to code” (which many have claimed and I still think it’s quite subjective) I’m starting to think there isn’t a worthy reason to go through all the pain of learning Dojo, yes, and I will say it again it’s a pain even with the good will to learn it, I’m having such a heavy complain because I didn’t have this problem moving from JQuery to Mootools which, both are a “import/customize the file, use whatever you need, learn as you need”, you don’t have to learn to code again, you just have to use the libraries (though Mootools it’s more suitable for large projects because it’s more organized).

    I’ve been following your blog since quite some time and I would like to know your opinion about this (and a possible solution).

    • Skullcap

      I agree with Cesar. I have been trying to be efficient at working with dojo and dijit for 6 months (!)… and I still feel like an idiot with it. Maybe I am an idiot, I have never had this problem with any other framework/toolkit/api… Dojo leaves me scratching my head every fricking day. Dojo.core seems elegant, logical and straight forward to use. But dijit!! Oh my. Oh my oh my. I want to curl up into a ball and suck my thumb whenever I have to work on widgets or even worse, style my widgets (sadly, this is daily for the past few months; my thumb is quite raw by now). Look at me, I am ranting instead of working on my widgets.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!