Rache
Rache

Reputation: 217

How to make a forEach loop wait for each Ajax function to finish

I have an array of statements and I want to loop through each and submit to the server via an ajax call. I have to make sure each ajax request is executed before the next request is sent, because on the server side code, I am creating a unique key for this array of statements. What happens is before I can commit the first request, the second request comes in and I end up have 2 separate keys being created since it sees each of them as a first time request. I can't rely on timeout entirely and the code I have so far, continues with the loop.

function formPostSubmission(form){
    var parts = ['a', 'b', 'c'];
    var i = 0;
    parts.forEach(function(entry) {
        i++;
        datafield ={
            answer:entry,
            displayOrder:i,
            ajaxName:'statementPartialSubmit'
        };
        $.when(submitAjaxData(form, datafield)).then(function succesHandler(data){
            console.log("promise test "+data);
            console.log("Below request ");
        },
        function errorHandler(){
            console.log("Error occurred ");
        })
        console.log("Go for next ");
    });
}

function  submitAjaxData(form, datafield) {
    console.log(" called submitAjaxData  ");
    var loadurl = domainName + "/ajax-call-handler";
    return $.ajax({
        url: loadurl,
        method: "GET",
        data: datafield
    });
}

I was hoping to check response data for a success and then proceed within the loop. But this is how my console prints.

called submitAjaxData
Go for next
called submitAjaxData 
Go for next
promise test  
Below request
promise test 
Below request 

Upvotes: 3

Views: 6512

Answers (4)

darklightcode
darklightcode

Reputation: 2772

I wrote this function, PromiseAllSeq, to execute an array of promises sequentially.

You can pass a callback as the second parameter if you choose, but given your issue i've put together a working demo based on your code.

console.clear();
/**
 *  Promises will wait for each other and will return any[].
 *  If a promise is rejected it will stop.
 *  @param arr - (Promise<any> | ()=>Promise<any>)[] - Accepts promise or a function that returns a promise
 *  @param callback(response, index) - If callback 'returns' it will overwrite the current response. Useful for changing the response.
 *  @returns any[]
 */
const PromiseAllSeq = (arr, callback) => {

  let result = [];

  if (typeof callback !== 'function') {
    callback = () => {};
  }

  const rec = (arr) => {

    return new Promise((resolve, reject) => {

      if (arr.length) {

        (typeof arr[0] === 'function' ? arr[0]() : arr[0]).then(res => {

          let cb = callback(res, result.length);

          result = result.concat(typeof cb !== 'undefined' ? cb : res);

          arr.splice(0, 1);

          resolve(arr.length ? rec(arr) : result);

        }).catch(err => {

          reject(err);

        })

      } else {

        resolve(result);

      }

    })

  }

  return rec(arr);

}

function succesHandler(data, index) {

  // Here i can alter 'data'
  data['finishedPromiseNr'] = index;

  console.log("promise test ", data, index);

  console.log("Go for next ");

  return data;

}

function errorHandler(err) {
  console.log("Error occurred ", err);
}

function submitAjaxData(form, datafield) {
  console.log("called submitAjaxData  ", datafield);
  //var loadurl = domainName + "/ajax-call-handler";

  // Mock some requests with delayed responses
  // https://reqres.in/#console
  var loadurl = "https://reqres.in/api/users?delay=" + datafield.fakeDelay;
  return $.ajax({
    url: loadurl,
    method: "GET",
    data: datafield
  });
}

function formPostSubmission(form) {
  var parts = ['a', 'b', 'c'];
  var i = 0;
  let poll = [];
  parts.forEach(function(entry) {
    i++;

    // 'datafield' was not assigned with var or let
    // Reassigning it on each loop will allow 'datafield' to pass in the anonymous function below
    let datafield = {
      answer: entry,
      displayOrder: i,
      ajaxName: 'statementPartialSubmit',
      fakeDelay: parts.length - i
    };

    // Wrong: submitAjaxData is called, that means, the requests are sent immediately.
    // poll.push( $.when(submitAjaxData(form, datafield) );

    // Correct: Wrap and return $.when into a function otherwise 'submitAjaxData' will trigger the requests
    poll.push(() => $.when(submitAjaxData(form, datafield)));

  });

  PromiseAllSeq(poll, succesHandler).then(responseList => {

    console.log('final response');
    console.log(responseList);

  }).catch(errorHandler)


}

formPostSubmission(null);
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

Upvotes: 0

Sari Masri
Sari Masri

Reputation: 226

you could use this solution by using await , for example :

for (let f of files) {
    await $.ajax({/* your ajax config*/ });
 }

Upvotes: 2

Joby Joseph
Joby Joseph

Reputation: 11

async: false 

is the best option in your case

Upvotes: 0

Sari Masri
Sari Masri

Reputation: 226

use promises :

 var promises = [];
 for (var i = 0; i < $total_files; i++) {
 // jQuery returns a prom 
   promises.push($.ajax({
  /* your ajax config*/
  }))
 }

Promise.all(promises)
.then(responseList => {
 console.dir(responseList)
 })

Upvotes: 3

Related Questions