14

I am trying to load a bunch of data from an API Async and when all the data is loaded I want to trigger an event that all the data is loaded. The problem I am having is that the API I am using limits the number of response objects to five. And I would potentially need to retrieve 30-40 response objects.

So what I want to do is create a when - then statement that loops trough the data items and makes request for every five items then when all the items are loaded I want to fire a loaded event. The issue I am having is that the when-then statement is completing before the success of the ajax request.

onto the code I have tried.

 function loadsLotsOfStats(stats, dataType, eventName, dataName, callback) {
     var groupedStats = [];
     while (stats.length > 0) {
         groupedStats.push(stats.splice(0, 5).join('/'));
     }
    j$.when(
        groupedStats.forEach(function (d) {
            loadJSONToData(model.apiUrl.replace("{IDS}", d), "json", "", dataName, function (d) { /*console.log(d);*/ }, true)
        })
    ).then(function () {
        j$(eventSource).trigger('dataLoaded', eventName);
    });

The loadJSONToData function is basically just a wrapper function for an Async $.ajax.

so yeah the event is getting triggered before the data is actually loaded. Also for some reason if I try to put for loop right in the when( statement it through a syntax error?

Does anyone have any advice on how I could make a bunch of Ajax requests and wait until they all are compeleted before triggering an event? Or a away to fix what I currently have?

Thanks in advance for the help.

recneps
  • 1,285
  • 5
  • 16
  • 27
  • You can also use `if(condition.length < 10) { // code to execute }`. – Afzaal Ahmad Zeeshan Aug 25 '13 at 01:33
  • Possible duplicate of [Trying to make 2 Ajax calls via JQuery, and then prepending the data (taken from Freecodecamp Twitch Project)](http://stackoverflow.com/questions/42425885/trying-to-make-2-ajax-calls-via-jquery-and-then-prepending-the-data-taken-from) – Mehdi Raash Feb 23 '17 at 21:58

2 Answers2

23

It's possible to do what you're asking. HOWEVER, the server you are sending your requests to probably has a reason for the limit they enforce. As someone who works in web development and has seen first hand how annoying DDOS, scraping, and other abuses of APIs can be, I would suggest conforming to their limit.

That being said, here's how you can do it.

$.ajax actually returns a deferred object, so you can use that to your advantage. Also $.when can accept any number of deferred objects. Combining these two facts can solve your problem.

var deferreds = [];
$.each(groupedStats, function(index, stat){
    deferreds.push(
        // No success handler - don't want to trigger the deferred object
        $.ajax({
            url: '/some/url',
            data: {stat: stat},
            type: 'POST'
        })
    );
});
// Can't pass a literal array, so use apply.
$.when.apply($, deferreds).then(function(){
    // Do your success stuff
}).fail(function(){
    // Probably want to catch failure
}).always(function(){
    // Or use always if you want to do the same thing
    // whether the call succeeds or fails
});

Note that this is not a race condition. Although $.ajax is asynchronous, $.each is not, so your list of deferreds will be the total list before you get to $.when and $.then/$.fail/$.always will only be triggered once they all complete.

EDIT: I forgot to add the splitting by 5s, but this illustrates the general idea. You can probably figure out from here how to apply it to your problem. Incidentally, you could just use array.splice(0,5) to get the next 5 results from the array. .splice is safe to use; if the total number of elements is less than 5, it will just take all the remaining elements.

tandrewnichols
  • 3,456
  • 1
  • 28
  • 33
  • 2
    Thumbs up, this works exactly as I need it to. Great answer and good explanation. Thanks for the time. And yes I will look into a different way of accessing the data I need, since your right that limit is there for a reason. – recneps Aug 25 '13 at 20:40
  • great explanation! Works like a charm. – Mike Mar 01 '16 at 20:07
  • I'm sorry if I am missing it, but how do you catch and evaluated the response – atwellpub Mar 18 '16 at 00:39
  • The then function argument will include the collective responses. – tandrewnichols Mar 18 '16 at 02:40
  • always() is called only once for the first ajax post. How can I catch when all the calles in the loop are finished, whether succeed or not? – marlar Jan 13 '17 at 18:34
  • I've never specifically done that, but from generic experience with promises, it doesn't work that way. `then` gets called when all of the requests succeed, but I'm pretty sure the error handler gets called as soon as any request fails, so there's no way to aggregate multiple failures. There are ways to do that manually, but you should ask that as a separate question if you need help getting it working. But if someone else knows more about promises than I do, feel free to chime in here if I'm wrong. – tandrewnichols Jan 13 '17 at 19:49
  • This is one of those answers where when you see it, you think "it's so simple", but in reality is not always easy to intuit. – William Jul 11 '18 at 18:28
  • Doesn't the .then function need to take a parameter? Actually doesn't need to take as many parameters as calls that were made to get all the data back? Do you know an easy way of accomplishing this? – Jake Oct 24 '18 at 01:34
  • The promise spec says that `.then` always takes exactly one parameter. In the case of multiple promises (as in Promise.all) that one parameter is an array containing each separate promise response. But I don't know how compliant jquery's API is with the promise spec. The parameters were omitted in this example for brevity. – tandrewnichols Oct 24 '18 at 14:30
  • I know it's old, but if these were GET requests, how would the returned data be accessed? Would the `responseText` property of each object in the `deferreds` array be the correct way to get the data, or is there a more idiomatic way to get the data? – K. Shores Jun 27 '19 at 14:44
  • `$.when(deferred1, deferred2).then(function(res1, res2) {`. See https://api.jquery.com/jquery.when/. Not sure how that works if those deferreds are http requests - do they only return data and not status? Or do they return a meta object with data and status properties. Not positive, but this should get you started. – tandrewnichols Jun 27 '19 at 16:06
  • you can use the spread operator instead of .apply: `.when( ...deferredResults ).then( function( ..data ) { } );` – photocurio Apr 21 '20 at 10:26
  • You can now, yes, depending on what browsers you support. – tandrewnichols Apr 21 '20 at 18:04
1

You can use Async.js libray. and try the each function.

Jake Lin
  • 11,146
  • 6
  • 29
  • 40