62

I have a three layer deep chain of deferred ajax calls, and ideally they are going to kick the promise all the way up when the deepest layer finishes (makes me thing of Inception... "we need to go deeper!").

The problem is that I'm sending off many ajax requests (possibly hundreds) at once and need to defer until all of them are done. I can't rely on the last one being done last.

function updateAllNotes() {
    return $.Deferred(function(dfd_uan) {
        getcount = 0;
        getreturn = 0;
        for (i = 0; i <= index.data.length - 1; i++) {
            getcount++;
            $.when(getNote(index.data[i].key)).done(function() {
                // getNote is another deferred
                getreturn++
            });
        };
        // need help here
        // when getreturn == getcount, dfd_uan.resolve()
    }).promise();
};
Tushar
  • 85,780
  • 21
  • 159
  • 179
brittohalloran
  • 3,534
  • 4
  • 28
  • 27
  • 2
    http://stackoverflow.com/questions/3709597/wait-until-all-jquery-ajax-request-are-done/9350515#9350515 – Benjamin Apr 04 '13 at 01:20

3 Answers3

116

You can use .when(), and .apply() with multiple deferred. Extremely useful:

function updateAllNotes() {
    var getarray = [],
        i, len;

    for (i = 0, len = data.length; i < len; i += 1) {
        getarray.push(getNote(data[i].key));
    };

    $.when.apply($, getarray).done(function() {
        // do things that need to wait until ALL gets are done
    });
}
Tushar
  • 85,780
  • 21
  • 159
  • 179
brittohalloran
  • 3,534
  • 4
  • 28
  • 27
  • 8
    You can also _accept_ your own answer. By doing this you can help others who are facing a similar problem, because they will see that this problem has been solved in their search results. :) – Exa Jul 07 '11 at 06:56
  • Is there any reason to have specified `$` as the first argument to apply? I get that it's the context of the call, but isn't the normal context for a jQuery call `window` or perhaps even `null`? – Ryley Jun 19 '13 at 15:45
  • 4
    This helps to clarify the answer above a bit: http://stackoverflow.com/questions/14777031/what-does-when-apply-somearray-do – StackExchange What The Heck Jun 21 '13 at 11:26
27

If you refer to jQuery.When doc, if one of your ajax call fails, fail master callback will be called even if all following ajax call haven't finished yet. In this case you are not sure that all your calls are finished.

If you want to wait for all your calls, no matter what the result is, you must use another Deferred like this :

$.when.apply($, $.map(data, function(i) {
    var dfd = $.Deferred();
    // you can add .done and .fail if you want to keep track of each results individualy
    getNote(i.key).always(function() { dfd.resolve(); });
    return dfd.promise();
});
Mordhak
  • 2,646
  • 2
  • 20
  • 14
  • I don't think always helps at all as it [still runs before the promises are fulfilled](http://jsfiddle.net/katowulf/J7H6M/). – Kato Dec 04 '12 at 23:16
  • 2
    Yes it help if you use the same code as me and wrap your promises. [I've updated your fiddle](http://jsfiddle.net/J7H6M/2/) with my example, and it works perfect. in your link, you simply did the same as brittohalloran, two promises not wrapped. In this case, if the first one fails, the fail/always callbacks will be called even if next promises are not finished. – Mordhak Dec 05 '12 at 10:22
7

Thanks for the answer brittohalloran. I'm also using Underscore, so I was able to apply your solution very cleanly with map, kinda like this:

$.when.apply($, _.map(data, function(i) {
    return getNote(i.key);
})).done(function() {
    alert('Be Happy');
});

Wicked useful.

Travis Watson
  • 1,729
  • 1
  • 18
  • 25