4

I've looked through many similar questions but the proposed solutions do not work in all cases as expected. The following code works fine when all ajax calls are successfully done but if any of ajax calls happens to fail, then onComplete is immediately called:

var deferredArray = $('.preload').map(function() {
    return $.get(this.href)
});
$.when.apply($, deferredArray).then(onComplete, onComplete);

So there can be two cases:

  • all deferred calls are successful, then onComplete is called afterwards - works fine;
  • some deferred call fails (returns HTTP 400 Bad request), then onComplete is called immediately not waiting for other deferred calls.

The second case represents my problem. It should always wait for all calls to complete regardless of the status before calling onComplete.

I use jQuery 1.7.1 as it's built into the framework. If the issue is due to the version, I can upgrade but I'd prefer to keep the current version.

lagivan
  • 2,689
  • 22
  • 30
  • Have you looked into http://api.jquery.com/ajaxcomplete/ ? – khollenbeck Jun 15 '15 at 20:25
  • @KrisHollenbeck but ajaxComplete event is triggered on each completed Ajax request. I need it to be triggered only once when all my Ajax requests are completed. This is why I have to use `when`. – lagivan Jun 15 '15 at 20:33
  • Sorry, I meant to post the `ajaxStop()` link. I have used `ajaxStop()` to run stuff after all ajax events have finished. Usually this works if I need to do some sort of layout related JavaScript work and ensure the data is in place first. But it might not work for your use case. https://api.jquery.com/ajaxStop/ – khollenbeck Jun 15 '15 at 20:39
  • @lagivan See http://stackoverflow.com/questions/28131082/jquery-ajax-prevent-fail-in-a-deferred-sequential-loop , http://stackoverflow.com/questions/30790604/jquery-always-callback-firing-too-soon – guest271314 Jun 15 '15 at 20:43
  • I've not tried this yet. It might work indeed but it does not look like the right way. One drawback is it's global and I'd like to track not all Ajax calls but just those several `preload` calls. Moreover, after `preload` calls are completed, I'm going to trigger several more Ajax calls. So there will be one at least more `ajaxStop` event. I can filter that out of course but again it looks a dirty solution. – lagivan Jun 15 '15 at 20:45
  • Yeah I am not sold on it being the right way either. Maybe the "brute force" way. But it has worked for me in times when I thought the right way would have been to use defferred or promises. – khollenbeck Jun 15 '15 at 20:46
  • 1
    Looks like this question may have been asked too. See here.. http://stackoverflow.com/questions/5824615/jquery-when-callback-for-when-all-deferreds-are-no-long-unresolved-either-r – khollenbeck Jun 15 '15 at 20:49

2 Answers2

2

You could try ajaxStop(). Allbeit not the most elegant solution.

$( document ).ajaxStop(function() {
  // do some stuff now that all the ajax stuff has stopped
})

Also note:

If $.ajax() or $.ajaxSetup() is called with the global option set to false, the .ajaxStop() method will not fire.

khollenbeck
  • 16,028
  • 18
  • 66
  • 101
2

I'm not sure if this outside of the scope of the types of solutions you're looking for, but I've recently been taking advantage of bluebird.js to wrap all my jquery ajax calls.

For example:

Promise.resolve($.get("http://www.google.com")).then(function() {});

What this allows me to do is then perform a Promise.all which will then wait for all the ajax calls to be complete. Each individual promise can pass or fail, but .then (or .spread) on the .all will only be called if they all succeed.

var aPass = function(){
    return Promise.resolve($.get("/echo/json/"))
};

var aFail = function(){
    return Promise.resolve($.get("/echo/jsonFake/")) // this will fail
};

var allPass = [aPass(), aPass(), aPass()];

var oneFails = [aPass(), aPass(), aFail()];


Promise.all(allPass).spread(function(a,b,c){
    console.log("All Done!");
}).catch(function(){
    console.log("NEVER FAILS!");
});;

Promise.all(oneFails).spread(function(a,b,c){
    console.log("never goes in here");
}).catch(function(){
    console.log("FAILED");
});

Here is a jsfiddle demonstrating this: http://jsfiddle.net/ys598t4s/2/

RoboKozo
  • 4,981
  • 5
  • 51
  • 79