Take the 2-minute tour ×

I'm getting an unexpected result when using $.when() when one of the deferred operations does not succeed.

Take this JavaScript, which created 2 deferreds. The first one succeeds and the second one fails.

var f1 = function() {
    return $.Deferred(function(dfd) {
        dfd.resolve('123 from f1');

var f2 = function() {
    return $.Deferred(function(dfd) {
        dfd.reject('456 from f2');

$.when(f1(), f2())
    .then(function(f1Val, f2Val) {
        alert('success! f1, f2: ' + JSON.stringify([f1Val, f2Val]));
    .fail(function(f1Val, f2Val) {
        alert('fail!    f1, f2: ' + JSON.stringify([f1Val, f2Val]));

Run it yourself: http://jsfiddle.net/r2d3j/2/

I get fail! f1, f2: ["456 from f2", null]

The problem is that in the .fail() callback the value passed with the f2() rejection, is being routed to the first argument, where i expect the f1Value. Which means that I don't really have a way of know which deferred object actually posted that reject(), and I also dont know which operation that failure data actually belongs to.

I would have expected that .fail() would get arguments null, '456 from f2' since the first deferred did not fail. Or am I just not doing deferreds right way here?

How do I know which deferreds failed, and which rejection arguments belong to which failed deferred if the argument order in the callback is not respected?

share|improve this question

4 Answers 4

up vote 4 down vote accepted

Internally, the "reject" and "fail" paths are handled by two totally separate queues, so it just doesn't work the way you expect.

In order to know which original Deferred failed from the "when()" group, you could have them pass themselves along with the ".reject()" call as part of an object literal or something.

share|improve this answer
Hmm, not quite how I would have designed it, but I guess that makes my result make sense. –  Alex Wayne Apr 1 '11 at 21:27

$.when() will execute the failed callback (2nd parameter passed to then()) immediately if any one of the parameters fails. It's by design. To quote the documentation:


In the multiple-Deferreds case where one of the Deferreds is rejected, jQuery.when immediately fires the failCallbacks for its master Deferred. Note that some of the Deferreds may still be unresolved at that point. If you need to perform additional processing for this case, such as canceling any unfinished ajax requests, you can keep references to the underlying jqXHR objects in a closure and inspect/cancel them in the failCallback.

There's actually no built-in way of getting a callback that waits untils all of them are finished regardless of their success/failure status.

So, I built a $.whenAll() for you :) It always waits until all of them resolve, one way or the other:


$.whenAll(a, b, c).then(callbackUponAllResolvedOrRejected);
share|improve this answer
You say "will execute the then() callback immediately", but the quote you include says "immediately fires the failCallbacks" - so which is it? \-: –  hippietrail Aug 8 '12 at 10:12
@hippietrail It's both. then() fires when deferred is "resolved" OR "rejected". This classifies as a rejection, so it fires. –  InfinitiesLoop Aug 8 '12 at 16:16
no, it doesn't fire the then callback .. it fires the fail callback. (which technically can be the 'second' callback passed to 'then', but in any case it's not the same as the 'then' callback which most people reading your answer will assume is the 'success' callback). –  hasenj Jan 2 at 21:31
@hasenj I edited it to be more clear, thanks :) –  InfinitiesLoop Jan 3 at 3:35

I've faced this same problem, and I dealt with it by using the .always callback and inspecting my array of deferred objects. I had an unknown number of ajax calls, so I had to do the following:

// array of ajax deletes
var deletes = [];
$checkboxes.each(function () {

$.when.apply($, deletes)
  .always(function () {
      // unfortunately .fail shortcircuits and returns the first fail,
      // so we have to loop the deferred objects and see what happened.

      $.each(deletes, function () {
          this.done(function () {
          }).fail(function () {

The deleteFile method returns a promise, which has .done or .fail callbacks.

This allows you to take action after all deferreds have completed. In my case I'm going to show a delete file error summary.

I just tried this, and unfortunately I had to put a interval timer to check that they were all truly done after my $.each on the deferred objects. This seems odd and counterintuitive.

Still trying to understand these deferreds!

share|improve this answer
always will also "short-circuit" if any of the deferreds fail. –  Chloraphil Aug 6 '13 at 18:07


This will always reject if given multiple inputs. rejected = true; should be rejected |= reject;

share|improve this answer
Thanks, I only now noticed this reply since it wasn't made on my answer. I fixed it... better late then never? –  InfinitiesLoop Jan 3 at 3:40

Your Answer


