When multiple Deferred objects are passed to jQuery.when, the method returns the Promise from a new "master" Deferred object that tracks the aggregate state of all the Deferreds it has been passed.
The method will either
- resolve its master Deferred as soon as ALL the Deferreds resolve, or
- reject its master Deferred as soon as ONE of the Deferreds is rejected.
If the master Deferred is resolved (ie. ALL the Deferreds resolve), it is passed the resolved values of all the Deferreds that were passed to jQuery.when. For example, when the Deferreds are jQuery.ajax() requests, the arguments will be the jqXHR objects for the requests, in the order they were given in the argument list:
$.when( $.getJSON('foo'), $.getJSON('bar') ).done(function(foo, bar) {
// foo & bar are jqXHR objects for the requests
});
In the multiple Deferreds case where one of the Deferreds is rejected, jQuery.when IMMEDIATELY FIRES the fail callbacks for its master Deferred, even if some of the Deferreds may still be unresolved at that point:
$.when( $.getJSON('foo'), $.getJSON('bar') ).fail(function(req) {
// req is the jqXHR object for one of the failed requests
});
I need to fire a callback when all the Deferreds passed to jQuery.when are no longer 'unresolved' (ie. all are either 'resolved' or 'rejected'). I could send JSON objects with 200 OK codes (instead sending JSON with 404 Not Found error status codes) and determine success/error in the done() method, but I'd prefer keeping my API RESTful. How can I accomplish this?
@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.
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.
An improvement on Leo Hernandez's solution for more general use cases that don't simply involve fetching resources from a server, which for example can include events triggered by user interactions, or asynchronous jQuery UI calls (e.g. slideUp() and slideDown()). See https://jsfiddle.net/1trucdn3/ for enhanced use case.
The improvement allows us to pass in non-Deferred values into the array argument. This was something that you could do with $.when(). Also, I cleaned up the output you get in the callback function to be more inline with how the original $.when() method works, in case you just want to get back the result regardless of the status. Leo's solution would pass the entire deferred object as a result, which you then need to dig into to find the information you need.
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: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 afunction() { ... }
block.@Alnitak and @DazWilkin answers are great! But I personally prefer functional style so here is a functional version for arbitrary number of promises:
Compared to @DazWilkin answer, I use
map
function instead offoreach
.I've recently made a plugin that may help. I call it
$.whenAll
.$.whenAll - https://gist.github.com/4341799 (tests)
Sample usage:
I found a solution where I have 2 requests in a when and am able to access individual successes even when one of the requests fail: