Knowing when forEach loop finished

Viewed 448

how I know when my code finished the loop? After it finished I have to run some more code, but it can only run when all the things I wrote there completely finished.

obj.data.forEach(function(collection) {
                var serialized_array = collection['quiz_ids'];
                quiz_collections.push([collection['id'], collection['name'], collection['type'], collection['category'], serialized_array.split(',')]);

                serialized_array.split(',').forEach(function(ite) {
                  query('./php/query.php', 2, [['quiz_id', ite]]).then(function(quiz_obj) {
                    if (quiz_obj.data[0] == 'false') {

                    }
                    else {
                      fetch(quiz_obj.data[0]['data']).then(function(resp) {
                        return resp.json();
                      })
                      .then(function(data) {
                        console.log(data);
                        quiz_data.push([quiz_obj['id'], quiz_obj['name'], quiz_obj['version'], quiz_obj['data'], data]);
                      });
                    }
                  });
                });
              });   

If it helps. My obj.data in the first line looks like this:
the object
And here is my query function:

function query(url, cmd_type, data_array) {
  var request = new XMLHttpRequest();
  var params= '';
  params = params + 'cmdtype=' + encodeURIComponent(cmd_type) + '&';
  if (data_array) {
    data_array.forEach(function(item) {
      params = params + item[0] + '=' + encodeURIComponent(item[1]) + '&';
    });
  }

  console.log(params);

  return new Promise(function(resolve, reject) {
    request.open('POST', url, true);
    request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    request.onreadystatechange = function() {
      if (request.readyState === XMLHttpRequest.DONE) {
        if (request.status === 200) {
          var response = JSON.parse(request.response);
          resolve(response);
        }
        else {
          resolve({error: 'Cant connect!'});
        }
      }
    };
    request.send(params);
  });
}

Thanks in advance
Filip

3 Answers

You can use reduce to run promises one after another rather than forEach.

Example:

const data = [1, 2, 3, 4, 5];

const loopResult = data.reduce(async (previous, value) => {
  // Wait for previous loop to finish
  await previous;

  // Return new promise
  return new Promise((resolve) => {
    // Promise code
    // Make HTTP fetch here...
    resolve();
  });
}, Promise.resolve());

loopResult.then(() => {
  console.log('loop finished');
});

Alternatively, if your loop does not need to be in order, you can use Promise.all:

const loopResult = Promise.all(
  data.map((value) => new Promise((resolve) => {
    // Promise code
    // Make HTTP fetch here...
    resolve();
  }))
);

loopResult.then(() => {
  console.log('loop finished');
});

One way to do this would be Promise.all.
You can push your fetch promises into an array and once the forEach loop is done, call Promise.all on it:

const promises = [];

// start loop
const promise = query(...);
promises.push(promise);
// end loop
Promise.all(promises).then((values) => doSomething(values));

This solves your problem, however there are nicer ways to do this, e.g. using a RxJS forkJoin. RxJS is very helpful when processing more complex asynchronous code.

the promise should have a .then method which is called when it completes. the then method will take in a function to call.

var promise = // your code that gets the promise ( a call to query)
promise.then((values) => {//code to run afterwards})
Related