32

I'm having an issue with using jQuery.when() to wait for multiple ajax requests to finish before calling another function.

Each ajax request will get JSON data, and looks something like this:

   function loadData(arg){
        var ajaxCall = $.ajax(
            URL // depends on arg
        )
       .error( .... );
       return ajaxCall;
   }

When the request is called, the return value (ajaxCall) is added to a list called ajaxRequests.

    ajaxRequests = [];
    ajaxREquests.push(loadData(arg))

When all the requests have been made, I'm trying to pass ajaxRequests to $.when in order to wait for all requests to complete.

        var defer = $.when.apply($, ajaxRequests);
        defer.done(function(args){
            for (var i=0; i<args.length; i++){
                inst.loadData($.parseJSON(args[i].responseText));
            }
            inst.draw();
        });

inst is an object that loads and draws graphs based on JSON data.

The problem is that it doesn't seem to be actually waiting for the requests to finish - args[i] is an object, but responseText is undefined when the code runs. If I save args[i] and access it later from the console, it works.

I suspect the problem is related to using .when with an arbitrary number of arguments, as all the examples I've seen on the web give it a pre-defined argument list.

I'm not sure if using apply was the right idea or not, but either way it doesn't work properly and behaves erratically (browser-dependent).

Any help would be greatly appreciated.

Please let me know if more information is required.
I'm using jQuery 1.5

Nakilon
  • 34,866
  • 14
  • 107
  • 142
Alex
  • 18,332
  • 10
  • 49
  • 53
  • 1
    Maybe it's just me, but the syntax you have in your code doesn't seem to match that from the jQuery API (http://api.jquery.com/jQuery.when/). Perhaps you want to try that syntax and see if it works? – seeming.amusing Mar 26 '12 at 01:33
  • I believe his point was that he is not using it the way it is shown in the docs because he has a variable number of arguments. – Greg Ennis May 02 '12 at 03:53
  • Related: [Pass in an array of Deferreds to $.when()](http://stackoverflow.com/q/5627284/1048572) and [Looping through jQuery Deferreds after all promises have been called](http://stackoverflow.com/q/21376083/1048572) – Bergi Jul 27 '14 at 12:33

4 Answers4

40

Although Alex did indeed provide a solution to his problem, I found following it a bit difficult. I had an issue similar to his that I solved, and I wanted to share my solution for anyone else who needs to process a variable number of ajax requests.

// Array of requests
var requests = Array();
requests.push($.get('responsePage.php?data=foo'));
requests.push($.get('responsePage.php?data=bar'));

var defer = $.when.apply($, requests);
defer.done(function(){

    // This is executed only after every ajax request has been completed

    $.each(arguments, function(index, responseData){
        // "responseData" will contain an array of response information for each specific request
    });

});
Andy Corman
  • 673
  • 7
  • 12
  • 3
    thanks Andy, it works great so long as all the requests succeed, but how do you handle it if one succeeds and one fails? – Martin Charlesworth Jan 20 '14 at 12:12
  • 6
    Great solution. One thing I noticed is that arguments will not be an array of [data,status,jqXHR] arrays if there is only one request in the deferred array, but rather just a single [data, status, jqXHR] array. Just something to take note of if you expect only a single request sometime. – cgat May 23 '14 at 17:12
  • Does not work any longer with v1.11.3. Done() get's fired once all requests have been issued. – Manuel Arwed Schmidt Feb 22 '16 at 14:02
  • What determines the order of the items in the `responseData` array? Are they guaranteed to be similar each time? – John Skiles Skinner Aug 27 '19 at 21:20
9

In addition to Andy Corman's answer (I am yet unable to reply to a post, I think ...), if you only have one request, the response information will be passed directly to the defer.done - function as an argument; So you need to provide an if for that case:

// Array of requests
var requests = Array();
requests.push($.get('responsePage.php?data=foo'));

var defer = $.when.apply($, requests);
defer.done(function(){

    // This is executed only after every ajax request has been completed
    if (requests.length == 1)
        // "arguments" will be the array of response information for the request
    else
        $.each(arguments, function(index, responseData){
            // "responseData" will contain an array of response information for each specific request
        });
});
DHainzl
  • 1,043
  • 9
  • 10
4

I think I've worked it out now - the problem was in processing the returned arguments. .done was getting passed three arguments only - the response text, status and jqXHR object. I was expecting it to get passed the jqXHR object resulting from each query.

I've solved it by moving the callback code for each query to a separate function (i.e. the ajax call in loadData is now specifying a callback function which does the '.loadData(...)' call) and the only thing being done by the .done call is the inst.draw(). This seems to work fine: the individual callback's are each execute before .done().

I'm not sure if that's exactly how it's supposed to work, but it seems to be doing the job.

Nakilon
  • 34,866
  • 14
  • 107
  • 142
Alex
  • 18,332
  • 10
  • 49
  • 53
1

Try ajaxStart and ajaxStop, which have listeners for open ajax requests.

http://api.jquery.com/ajaxStart/ http://api.jquery.com/ajaxStop/

user1289347
  • 2,397
  • 1
  • 13
  • 16