Skip to content
briancavalier edited this page Feb 1, 2012 · 6 revisions

when() + jQuery Deferred

when.js works with jQuery Deferred. Even though jQuery Deferred passes the Promises/A duck-type test, it is not Promises/A compliant. However, when you pass any jQuery thenable (jQuery Deferred or the result of calling .promise()) to when.js's when(), it will be assimilated, and when() will return a Promises/A compliant promise. Thus, any downstream .then() calls will be Promises/A compliant.

when.js's when() and Promise implementation will also assimilate any jQuery Deferred (in fact, any non-compliant promise) that is returned by a callback or errorback in a chain of promises, thus preserving Promises/A forwarding even when non-compliant promises get into the mix.

Here is an example of how when.js assimilates jQuery thenables and guarantees Promises/A behavior.

All of when.js's promise array handling methods, like when.all(), when.reduce(), etc. will work with a mixed array of jQuery thenables, when.js Promises or Deferreds, any other Promises/A promises, and immediate values. This is one of the advantages of when() (read more here).

when.defer() + $.when()

Unfortunately, $.when() does not accept Promises/A promises. It accepts any object that has a .promise() method. So, there is no way to pass a when.js Promise or Deferred, or any Promises/A compliant promise directly to $.when().

To use $.when() with a Promises/A promise, you have to wrap it in an object that has a .promise() method:

var promise = methodThatReturnsAPromisesAPromise();

// Can't pass promise directly
// Wrap it in an object with a promise() method, so that $.when() will understand
$.when({
	promise: function() {
		// return compliant promise
		return promise;
	}
}).then(...);

Ideally, we'd like to see $.when() deal directly with Promises/A compliant promises, since it's a proposed CommonJS standard, but the object-with-a-promise-method approach is only jQuery-specific.

when.all() and $.when()

If you pass multiple jQuery Deferreds to $.when() it will join them--that is, it will return a promise that resolves when all the input Deferreds have resolved.

var joined = $.when(deferred1, deferred2)
	.then(function(result1, result2) {
		// do something with result1 and result2
	});

// subsequent callbacks can be registered via joined.then()

when.all() achieves the same goal. Passing an array of promises to when.all() will join them, returning a promise that will resolve when all the promises in the array have resolved.

var joined = when.all([promise1, promise2],
	function(results) {
		// do something with results[0] and results[1]
	}
);

// subsequent callbacks can be registered via when(joined, callback)
// or joined.then()

One difference between the two is how the joined callbacks are invoked. when.js always invokes callbacks with a single parameter, which in this case, is an array of the results, and $.when() invokes its callbacks with an argument list.

Using when/apply

There are, of course, valid use cases for both styles, so when.js provides a helper module when/apply that allows you to spread the callback parameters. Here is the example above, modified to use when/apply:

var apply = require('when/apply');

var joined = when.all([promise1, promise2],
	apply(function(result1, result2) {
		// do something with result1 and result2
	})
);