6

I'm trying to accomplish the following sequence of operations using JQuery in HTML.

  • A list of urls is constructed
  • Each of these urls is requested using $.getJSON(url) in parallel
  • Wait for all requests to complete or to fail (404 might happen)
  • Take the data of all completed JSON-Requests and do something.

I constructed the Java-Script code posted below. It works perfecly except if one of the requests fails due to a 404-error: Then, $.when does not run because it instantly aborts if a requests fails. Can you somehow override the ajax-requests, so they don't fail but instead return a empty source?

I already read this and this post, but it does not provide a solution where code can be run after all queries have completed.

function fetchData() {
    queries = [];
    //urls is initialized somewhere else containing all urls
    for(var url in urls) {
        queries.push($.getJSON(url));
    }

    $.when.apply(this, queries).done(function() {
        console.log("Finished all queries, got "+arguments.length+" results");
        //Now arguments is a array containing the results of all requests
    }

    //function returns instantly, no problem!
 }
Community
  • 1
  • 1
theomega
  • 31,591
  • 21
  • 89
  • 127
  • I'm not that experienced in jquery, but it seems you need query counter. I.e. Set a counter to the number of queries you will send. In the success, or corresponding error, function, decrement the counter (is unary decrement atomic in Javascript?). Once the counter reaches zero, you know all requests are done and you can process the results. Of course, you will have to cache the results of the queries for later processing once all queries are done. – ewh Apr 24 '11 at 22:10
  • Duplicate: http://stackoverflow.com/questions/1060539/parallel-asynchronous-ajax-requests-using-jquery – joeytwiddle Mar 16 '15 at 22:13

3 Answers3

2

I might do something like this:

$(document).ready(function(){
    var _q = ['/echo/json/', '/echo/json/', 'mybadurl'];
    var _d = [];
    for(u in _q)
    {
        $.ajax({
            url: _q[u],
            success: function(data){
                pushData(data);
            },
            error: function(x){
                pushData(x);
            }
        });
    }

    function pushData(d)
    {
        _d.push(d);
        if(_d.length == _q.length)
        {
            alert('were done');
        }
    }
});

Here, you're pushing data from either the success or error onto the array _d and testing to make sure you've received all replies before continuing (alert('were done');).

Of course, you could make this smarter by pushing an object that contains op state along with the payload into _d, but this was a quick hack together to demonstrate how I'd most likely go about doing what you're asking.

Fiddle here

Demian Brecht
  • 21,135
  • 5
  • 42
  • 46
1

Something like this:

function fetchData() {
    queries = [];
    //urls is initialized somewhere else containing all urls
    for(var url in urls) {
        queries.push($.getJSON(url));
    }

    var returned = 0;
    for (var i = 0; i < queries.length; i += 1) {
        call_ajax_somehow(queries[i], function () {
            console.log("callback called");
            returned += 1;
            if (returned === queries.length) {
                console.log("Got all " + queries.length + " of them");
            }
        });
    }
 }

You'd have to check the jQuery ajax spec on how to call it exactly and how to handle the errors (ie, how to specify the error callback) but it shouldn't be too hard to modify the code.

Halcyon
  • 57,230
  • 10
  • 89
  • 128
1

Something like this should work:

function fetchData() {
    var queriesRun = 0;
    var allData = [];

    var urls = ["http://stackoverflow.com", 
                "http://stackoverflow.com", 
                "http://stackoverflow.com", 
                "http://stackoverflow.com", 
                "http://stackoverflow.com/thisdoesnotexist"];

    var totalQueries = urls.length;

    var complete = function(jqXHR, textStatus) {
        queriesRun++;
        console.log(queriesRun);

        if(queriesRun == totalQueries) {
           console.log("all done");
           console.log(allData);
        }
    };

    for(var i = 0; i < urls.length; i++) {
        var url = urls[i];
        jQuery.ajax({
           url: url,          
           success: function(data, textStatus, jqXHR) {
              allData.push(data);
           },
           complete: complete
        });
    }
}

Since the complete handler runs after the error and success handler, queriesRun will get incremented each time the AJAX call completes. This handler also checks to see if the number of queries run is equal to the total number of queries. If it is, it will work work with the data.

I've added console.logs in the code so you can easily see what's going on (just run it in firebug).

Vivin Paliath
  • 94,126
  • 40
  • 223
  • 295