0

I have an AJAX call that gets called "i" amount of times. I want to execute the rest of the code only after the last AJAX processData callback function was finished (It fills values of the .csv into an array called "lines" and I need the finished array after all iterations have finished). So far it only works by using "setTimeout()" which is not a nice solution

for (var i = 0; i < options.length; i++) {
    (function(index) {
        $.ajax({
            type: "GET",
            url: options[index] + ".csv",
            dataType: "text",
            success: function(data) {
                processData(data, options[index], type)
            }
        });
    })(i);
}
setTimeout(function() {
    getAveragePercentages(lines);
}, 500)
Enzo B.
  • 2,341
  • 1
  • 11
  • 33
user3024814
  • 246
  • 3
  • 14
  • 3
    Possible duplicate of [Wait until all jQuery Ajax requests are done?](https://stackoverflow.com/questions/3709597/wait-until-all-jquery-ajax-requests-are-done) – Esko Jul 19 '18 at 09:15
  • I think "the rest of the code" is your getAveragePercentage(lines) code? if so, that should work if you just put it outside of your for loop actualy.. how do you start your for loop? by clicking a button? – Christopher Supertramp Jul 19 '18 at 09:16
  • it starts by clicking a button, correct. If I put it outside, the code of "getAveragePercentages()" will be executed before the ajax callbacks have finished – user3024814 Jul 19 '18 at 09:17
  • 1
    @ChristopherSupertramp That won't work since the loop will be done before all the ajax-calls finish. Ajax is by nature asynchronous – Esko Jul 19 '18 at 09:17
  • Promise.all() may help – Kishan Rajdev Jul 19 '18 at 09:19
  • $(document).ajaxStop(function () { getAveragePercentages(lines); }); is working too, but also not a nice solution, in case I wanna make more ajax calls, that don't wanna do getAveragePercentages afterwards – user3024814 Jul 19 '18 at 09:45
  • Promise.all() sounds interesting. I try to figure it out, but could need further help – user3024814 Jul 19 '18 at 09:47
  • @user3024814 Please check my solution. You may have to refactor it though – Kishan Rajdev Jul 19 '18 at 10:04

6 Answers6

4

You can use the JavaScript promise functionality.

Make AJAX request in the promise. Create an array which will contains all these promise.

Promise.all will be executed after all promise get resolved.

var promiseArr = [];
for (var i = 0; i < options.length; i++) {
    var promise = new Promise(function(resolve, reject) {
        (function(index) {
            $.ajax({
                type: "GET",
                url: options[index] + ".csv",
                dataType: "text",
                success: function(data) {
                    processData(data, options[index], type); resolve('outputIfany')
                }
            });
        })(i);
    });
    promiseArr.push(promise);
}
Promise.all(promiseArr).then(function(values) {
    getAveragePercentages(lines);
});
Tarun Khurana
  • 887
  • 8
  • 9
0

set up a counter and check it's value before calling your function

$("#counter").html("0");
for(var i=0;i<options.length;i++){
            (function(index){       
                $.ajax({ 
                    type: "GET",
                    url: options[index]+".csv",
                    dataType: "text",
                    success: function(data) {
                    processData(data, options[index], type)
                    var counter = $("#counter").html();
                    if( counter == options.length ){
                      getAveragePercentages(lines);
                    }
                     $("#counter").html(counter+1);
                   }
                });

            })(i);
        }
Me1o
  • 1,629
  • 1
  • 6
  • 8
  • The problem is I don't know how big the final counter has to be, options could be different kind of lengths. – user3024814 Jul 19 '18 at 09:21
  • @user3024814 then use `options.length` to compare.. check my updated answer – Me1o Jul 19 '18 at 09:24
  • 1
    this check will just fail, since it only checks once and if counter isn't at option.length at this time (which it won't) it will just skip. I don`t wanna make a loop relentlessly checking – user3024814 Jul 19 '18 at 09:28
  • you are right, edited, moved the checks to inside the loop.. as for your second point, this is how loops work.. it skips until it's condition is met.. this check will only fire with every ajax request.. WAY better than a setTimeout function. – Me1o Jul 19 '18 at 09:35
  • but that's not how it works. we need to wait for the success of the last callback (processData), which is asynchronous, which means the code after the $.ajax ()-call will not wait for the callback to get finished. – user3024814 Jul 19 '18 at 09:42
  • Even with the counter, this is still synchronous, so cannot ensure that `getAveragePercentages` is called after `processData` has been called for all indices. The version with `setTimeout` actually had a better chance to achieve that. – Lucas S. Jul 19 '18 at 09:44
  • @user3024814 then perform the check inside the success callback function.. *updated answer – Me1o Jul 19 '18 at 09:55
  • this might work. still a little less elegant since I would need to pass the options.length and make counter a global variable – user3024814 Jul 19 '18 at 10:00
  • not elegant at all.. but more reliable than a timeout function – Me1o Jul 19 '18 at 10:02
0
for (var i = 0; i < options.length; i++) {
    (function (index) {
        $.ajax({
            type: "GET",
            url: options[index] + ".csv",
            dataType: "text",
            success: function (data) {
                processData(data, options[index], type)
            }
        });
        counter = counter + 1;
    })(i);
    if (i == options.length) {
        getAveragePercentages(lines);
    }
}
Burhan Ibrahimi
  • 367
  • 3
  • 14
0

You can do something like this.

after last Loop Success call function

var totalRec = options.length;
for(var i=0;i<options.length;i++){
    (function(index){       
        $.ajax({ 
            type: "GET",
            url: options[index]+".csv",
            dataType: "text",
            success: function(data) {processData(data, options[index], type)


           if(i == (totalRec-1)){
              getAveragePercentages(lines);
           }
        }
        });
    })(i);
}

or

var totalRec = options.length;
    for(var i=0;i<options.length;i++){
        (function(index){       
            $.ajax({ 
                type: "GET",
                url: options[index]+".csv",
                dataType: "text",
                success: function(data) {processData(data, options[index], type)


            }
            });
        })(i);

     if(i == (totalRec-1)){
          getAveragePercentages(lines); // gets called only when condition is true
      }
    }
MyTwoCents
  • 7,284
  • 3
  • 24
  • 52
0

It is not a good practice to use a setTimeOut for wait the ajax call, in my experience I've been using recursive functions for doing this, in your case you can do the following:

var counter = 0;
function main()
{
    counter = 0;
    doAjaxCall(counter);
}

function doAjaxCall(counter)
{
    (function(index){       
                $.ajax({ 
                    type: "GET",
                    url: options[index]+".csv",
                    dataType: "text",
                    success: function(data) {
                       processData(data, options[index], type);
                       if(counter < options.length)
                       {
                           counter++;
                           doAjaxCall(counter); //We call the same function but with the next index
                       }
                       else
                       {
                          //The loop finished, countinue code after your for loop
                       }
                    }
                });
            })(i);
}
Brank Victoria
  • 1,447
  • 10
  • 17
  • I've seen that processData is also a callback function, in this case you can put the logic of if(counter < options.length) in tht function and call again the doAjaxCall(counter) – Brank Victoria Jul 19 '18 at 09:26
  • One thing to note is, that this will execute in sequence, meaning that the next network call will only ever start when the previous one was finished and processed. That's quite inefficient unless `processData` actually requires to be called in sequence. – Lucas S. Jul 19 '18 at 09:48
0

I added a function as a parameter. The AJAX calls the function when the load is completed.

function loadDoc(call_back_func) {
  const xhttp = new XMLHttpRequest();
  xhttp.onload = function() {
    json_data = JSON.parse(this.responseText);
    call_back_func();
  }
  xhttp.open("GET", "kanban_personal_template.json");
  xhttp.send();
}

function load_call_back()
{
    console.log(json_data);
}
loadDoc(load_call_back);
GK10
  • 343
  • 4
  • 10