Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call a function after all ajax requests are done when using a foreach statement in jQuery

Tags:

jquery

ajax

I have an each statement and inside it I call ajax to send some information to server.

$.each(array, function(k, v) {
   $.ajax({
       ...
   });
});

When all ajax requests are done then call a function here... How to do that ?

I cannot use async: false flag anymore because it is deprecated.

like image 798
Snake Eyes Avatar asked Sep 14 '15 06:09

Snake Eyes


1 Answers

Store the promises returned by $.ajax in an array, and then use $.when to wait for them all to complete:

var promises = [];
$.each(array, function(k, v) {
   promises.push($.ajax({
       ...
   }));
});
$.when.apply($, promises).then(function() {
    // They're all done now
});

or using $.map:

$.when.apply($, $.map(array, function(k, v) {
   return $.ajax({
       ...
   });
})).then(function() {
    // They're all done now
});

You'll want to look at done, then, and always to determine which is best for your scenario.

$.when's API is a bit broken designed for a slightly different use case, which is why we have to call it in that odd way. What it does is accept a bunch of promises as discrete arguments, and return a new promise that will be resolved when all of the promises you pass it are resolved. E.g.:

$.when(promise1, promise2, promise3).then(...);

But frequently we have an array of promises (such as in the above). Since it expects discrete arguments, we use Function#apply to call it, since Function#apply accepts an array and then calls the function with the entries from the array as discrete arguments.

Note: $.when resolves the promise it returns when all of the promises you give it are resolved, or rejects its promise when any of the promises you give it is rejected. So if one of the ajax calls fails, you'll get your callback right away, even if other calls are still outstanding.

If you don't want that, then you have to write your own function, which isn't hard; something a bit like this:

function waitForAll(promises) {
    var waitingFor = promises.length;
    var allGood = true;
    var oneResolved = oneSettled.bind(null, true);
    var oneRejected = oneSettled.bind(null, false);
    var d = $.Deferred();

    promises.forEach(function(promise) {
        promise.then(oneResolved, oneRejected);
    });

    return d.promise();

    function oneSettled(resolved) {
        if (!resolved) {
            allGood = false;
        }
        if (--waitingFor === 0) {
            if (allGood) {
                d.resolve();
            } else {
                d.reject();
            }
        }
    }
}

That just waits for them all, and then either resolves or rejects depending on whether any of them failed. You could take it further, resolving or rejecting with an array of results.


You can make $.when with an array a bit more convenient by giving yourself a utility function:

function whenAll(array) {
    return $.when.apply($, array);
}

...and then:

whenAll($.map(array, function(k, v) {
   return $.ajax({
       ...
   });
})).then(function() {
    // They're all done now
});

You could even add that to $, but of course, a future version of jQuery may define whenAll, so be cautious.

like image 59
T.J. Crowder Avatar answered Oct 26 '22 19:10

T.J. Crowder