19

I have multiple ajax queries running at the same time, and I want them to wait for the last one to return, and then run the success handler on all of the ajax calls. For a simplified example, consider:

$.ajax({//ajax call 1
    url:page1.php,
    success: function(data1){
        //do something with data1
    }
});

....

$.ajax({//ajax call 2
    url:page2.php,
    success: function(data2){
        //do something with data2
    }
});
//consider more than just two concurrent requests

Let's say that all of the requests are sent off at the same time. As they are asynchronous, they will return at different times. Let's say one request takes 100ms to return and the other request takes 3000 ms to return. I obviously don't know which one will return first or last. They all update the DOM in some manner and I want those changes shown to the viewer all at once on one update. How do I do this?

The best I can think of is to save data1 and data2 as global variables. And then have a counter variable that counts every time a success is returned. Then, upon counter==TOTAL_NUM_REQUESTS call a function like updateAll(), then runs through all of the global variables and puts them where they need to go. But this seems messy and prone to errors. Plus that would be a lot of global variables.

What I ideally want is to have the success handlers sleep upon returning, and then on my condition of counting all of them as returned, send wake up messages to all of them and they can resume executing. This seems the cleanest, but I'm not aware of any functionality like this in javascriptland.

Does anybody have any slick ideas for this?

ANSWER UPDATE

Thanks to Lee below, I was able to get a solution. My solution looked similar to his below. I build a list of async variables that were assigned to the $.ajax call. Initially the success handler of those ajax calls were still being called, so I removed them and put them in another function that was to be subsequently called by this when block I wrote. I passed results in to the function like this:

var results = [];
results.push(async1);
results.push(async2);
... for all the results ...

$.when.apply(this, results).done(function() {
   for(var i=0;i<arguments.length;i++){
     dataobject=arguments[i][0]
     if(dataobject.variousattribute)
       mySuccessHandlerFirstGuy(dataobject)
     else if(dataobject.anotherattribute)
       mySuccessHandlerSecondGuy(dataobject)
       //etc ....
   }
};

The arguments object took a little work to figure out what it was. It was a 2d array (list of lists). The first index represented the returned object corresponding to a given ajax request. It seems to be in order, but it would be best to have your server return something that you can look for and write an if/else block accordingly. Then, in that given element, there appears to be 3 elements within that list. The first being the value that was returned from the server, ie what you want. The second was always a string success, which you can presumably use to check if the call worked. And the third element in that list seems to be the initial request (though I'm not sure). This was of no use to me.

Anyway, I hope this helps somebody in the future. And thanks again to Lee to point me in the correct direction.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Landon
  • 4,088
  • 3
  • 28
  • 42
  • Possible duplicate of [Wait until all jQuery Ajax requests are done?](https://stackoverflow.com/questions/3709597/wait-until-all-jquery-ajax-requests-are-done) – cilf Aug 30 '17 at 06:48

3 Answers3

40

Use the jQuery $.when() function to run something when all the ajax calls finish:

jQuery.when docs

var async1 = $.ajax({//ajax call 1
    url:page1.php,
    success: function(data1){
        //do something with data1
    }
});

....

var async2 = $.ajax({//ajax call 2
    url:page2.php,
    success: function(data2){
        //do something with data2
    }
});

$.when(async2, async1).done(function(result2, result1) {
    ... do this when both are successful ...
});

Added in response to questions:

If you have a bunch of ajax calls you can use 'apply' like this:

var results = [];
results.push(async1);
results.push(async2);
... for all the results ...

$.when.apply(this, results).done(function() {
    ... use 'arguments' array to get results ...
});
Zenexer
  • 18,788
  • 9
  • 71
  • 77
Lee Meador
  • 12,829
  • 2
  • 36
  • 42
  • nice, did not know about it, first what was invoked in my mind was http://api.jquery.com/category/deferred-object/ – dmi3y Jan 22 '13 at 18:16
  • 'when' just builds a new deferred on top of all of its parameters so all the normal deferred methods work. The only tricky part is that it fails when the first one fails and the others may not be complete. 'done' is only executed if all succeed. (It's like 'fail' is an 'or' and 'done' for success is an 'and'.) – Lee Meador Jan 22 '13 at 18:19
  • this looks promising, `var async1` has to be global in scope, right? – Landon Jan 22 '13 at 18:24
  • @Landon the 'async1' type variables only have to be visible to the code that calls 'when'. They could be in the same function or they could be global. – Lee Meador Jan 22 '13 at 18:26
  • 1
    great, what if the `async2` call is in a loop that runs a dynamic amount of times? In that case, I would only have two instances (variables) in my code with ajax functions, but I would want it to make, say, 5 ajax calls before executing. What is the syntax for `$.when` in that case? – Landon Jan 22 '13 at 18:42
  • Store all the return values from all the ajax calls in an array. Then you call 'when' via 'apply' `$.when.apply(this, array)`. 'apply' is not jQuery but plain old javascript. It allows to specify the 'this' for use inside the function and the parameters as an array. – Lee Meador Jan 22 '13 at 20:26
  • 2
    I expanded on the solution in my initial question above for future readers. Thanks Lee for your help. – Landon Jan 24 '13 at 23:31
  • This is very helpful ! – Gary Gauh Nov 29 '17 at 07:37
  • nice solutions, its work for me – Shaymol Bapary Apr 16 '22 at 17:07
0

This can probably be done in a very similar fashion to what you've already proposed.

First, create global variables var sleep = true and var request_count = 0. Then start a timer that checks request_count against total number of requests every second or so. Then, your success functions can increment request_counter and then start looping until sleep == false. Then, when the monitoring function running via timer detects request_count == TOTAL_REQUESTS it sets sleep = false, and all your success functions continue about their business at that point.

Scott S
  • 2,696
  • 16
  • 20
  • can you clarify what you mean by looping until sleep==false? like a busy loop? do you mean something like `while(request_count – Landon Jan 22 '13 at 18:22
  • 1
    Yes, thats essentially what I was suggesting. But you're right, if you can avoid it that would be better, and it looks like Lee's suggestion is the right way to go. – Scott S Jan 22 '13 at 18:40
-1

A simple solution will be to use PreloaderQ module

https://www.npmjs.com/package/preloaderq

Use as below

var preloader = new PreloaderQ()
preloader.setEmptyCallback(function(){
    //all tasks are finished
})

preloader.setFirstTaskCallback(function(){
    //started first task
})

preloader.enqueueTask('ajax1')

$.ajax({//ajax call 1
    url:page1.php,
    success: function(data1){
        prealoader.dequeueTask('ajax1')
        //do something with data1
    }
});

preloader.enqueueTask('ajax2')

$.ajax({//ajax call 1
    url:page2.php,
    success: function(data2){
        prealoader.dequeueTask('ajax2')
        //do something with data2
    }
});

The emptycallback will be called after both are finished

Jayadeep KM
  • 151
  • 1
  • 7