jQuery.when - Callback for when ALL Deferreds are no longer 'unresolved' (either resolved or rejected)?

I think the easiest way to do this is to keep a secondary Deferred object around for each AJAX request, and ensure that that one is always resolved:

var d1 = $.Deferred();
var d2 = $.Deferred();

var j1 = $.getJSON(...).complete(d1.resolve);
var j2 = $.getJSON(...).complete(d2.resolve);

$.when(j1, j2).done(function() {
     // only fires if j1 AND j2 are resolved
});

$.when(d1, d2).done(function() {
     // will fire when j1 AND j2 are both resolved OR rejected
     // check j1.isResolved() and j2.isResolved() to find which failed
});

This is making use of the additional AJAX .complete() method which jQuery adds to its promises for AJAX methods, which is called for both resolved and rejected promises.

NB: d1.resolve works as a callback in its own right, it doesn't need to be wrapped in a function() { ... } block.


@Alnitak answer is clever and helped me erase a hack that I'd created in which I was somewhat artificially resolving a promise - regardless of underlying outcome - in order that I may use 'when' to batch up multiple requests and use 'done' to proceed regardless of their success/failure.

I'm "answering" Alnitak's answer in the hope of providing another use for his suggestion that supports an arbitrary number of underlying promises.

var asyncFunc, entity, entities, $deferred, $deferreds;
// ...
foreach (entity in entities) {
    $deferred = $.Deferred();
    $deferreds.push($deferred);
    asyncFunc(entity).done(...).fail(...).always($deferred.resolve);
}
// ...
$.when.apply($, $deferreds).done(...)

This is pseudo-JavaScript but, it should convey the approach. For some arbitrarily sized set of entities, create a deferred ($deferred) for each entity and push it onto an array ($deferreds), make the async call, add done/fail as desired but always include an 'always' that resolves this entity's $deferred. NB the 'always' receives the deferred's resolve function not its invocation.

The 'when' converts the $deferreds array into the argument list for 'when' and, since this set of deferreds is guaranteed to resolve (thanks to the always), it's now possible to define a 'done' that will be invoked once all the async calls complete regardless of whether these succeed/fail.


I've recently made a plugin that may help. I call it $.whenAll.

This extension treats all successes and failures as progress events. After all the promises have completed, the global promise is resolved if there were no errors. Otherwise the global promise is rejected.

$.whenAll - https://gist.github.com/4341799 (tests)

Sample usage:

$.whenAll($.getJSON('foo'), $.getJSON('bar'))
  .then(
    doneCallback
    ,failcallback
    // progress callback
    // the only problem is $.ajax.done/fail states call their callbacks 
    // with params in different locations (except for state)
    ,function(data, state, jqXhr) {
      if (state == 'success') {
        // do happy stuff
      }
      else { // error (fail)
        // `data` is actually the jqXhr object for failed requests
        // `jqXhr` is the text of the error "Not Found" in this example
      }
    }
  )
;