How to loop through Ajax Requests inside a JQuery When - Then statment?
Asked Answered
T

2

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.

Telling answered 25/8, 2013 at 1:8 Comment(2)
You can also use if(condition.length < 10) { // code to execute }.Oraorabel
Possible duplicate of Trying to make 2 Ajax calls via JQuery, and then prepending the data (taken from Freecodecamp Twitch Project)Granniah
K
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.

Khichabia answered 25/8, 2013 at 2:25 Comment(13)
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.Telling
great explanation! Works like a charm.Cercaria
I'm sorry if I am missing it, but how do you catch and evaluated the responseCaseous
The then function argument will include the collective responses.Khichabia
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?Auld
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.Khichabia
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.Tawnatawney
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?Dover
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.Khichabia
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?Scotism
$.when(deferred1, deferred2).then(function(res1, res2) {. See 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.Khichabia
you can use the spread operator instead of .apply: .when( ...deferredResults ).then( function( ..data ) { } );Haywoodhayyim
You can now, yes, depending on what browsers you support.Khichabia
M
1

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

Michellemichels answered 25/8, 2013 at 1:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.