How to use async/await in JavaScript loops

Posted by Redneckheaven on Tue, 08 Mar 2022 02:46:29 +0100

Iterating over loop items in a loop and handling asynchronous logic (i.e., API calls) are probably the two most common tasks we must perform as JavaScript developers. This article will discuss the best way to combine async/await with iterative logic.

Sometimes you want to run asynchronous operations in a for loop (or any other type of loop). So how to deal with this kind of situation? Let's have a look.

Read Promise in order suppose there is a list of files. We want to read and record the contents of each file in order. How? We can use for in asynchronous functions
... cycle. Look at the code snippet.

async function printFiles () {
  let fileNames = ['picard', 'kirk', 'geordy', 'ryker', 'worf'];
  for (const file of fileNames) {
    const contents = await fs.readFile(file, 'utf8');
    console.log(contents);
  }
}

Note that if you want to read files sequentially, you cannot use the forEach loop.

Use a simple example to illustrate in more detail.

async function someFunction(items) {
  items.forEach( async(i) => {
     const res = await someAPICall(i);
     console.log('--->', res);
  });
}
function someAPICall(param) {
    return new Promise((resolve, reject)=>{
      setTimeout(()=>{
        resolve("Resolved" + param)
      },param);
    })
}
someFunction(['3000','8000','1000','4000']);

In the above code, there is a simple asynchronous function called someFunction, which takes an array as a parameter, iterates over the array and issues an API request for each array item (through a fake API function, ha ha). At this point, we want to parse the API calls in order. The contents you want to print are as follows:

//The expected output is 3000, 8000, 1000, 4000, but in fact, what we see is not such output, but the following results

//Actual output 1000 3000 4000 8000
The forEach loop does not call the API in sequence, but calls the API one by one without waiting for the previous call to complete. That's why we get the promise parsed for the first time. This is the main reason why we don't use the forEach loop.

Instead, we can use the reduce function to traverse the array and parse promise in order. Let's take a look at the following example.

function testPromise(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(`Processing ${time}`);
      resolve(time);
    }, time);
  });
}

let result = [3000,2000,1000, 4000].reduce( (accumulatorPromise, nextID) => {
  return accumulatorPromise.then(() => {
    return testPromise(nextID);
  });
}, Promise.resolve());

result.then(e => {
  console.log("All Promises Resolved !!✨")
});

Isn't it simple? Another way to parse promise sequentially is to use an asynchronous generator.

async function* readFiles(files) {
  for(const file of files) {
    yield await readFile(file);
  }
};

Most modern browsers, Node 10 and later support generators.

Parallel parsing Promise
Next, let's look at how to parse promise in parallel. Back to the first example. Now we want to read the files in parallel instead of sequentially. In this case, we don't care about the printing order of the content in the console. Therefore, promise The all() function is used with map.

async function printFiles () {
  let fileNames = ['picard', 'kirk', 'geordy', 'ryker', 'worf'];
  await Promise.all(fileNames.map(async (file) => {
    const contents = await fs.readFile(file, 'utf8');
    console.log(contents);
  }));
}

Each async callback function call will return a promise. We save them and compare them with prmiss All() parses at one time in parallel.

Topics: Javascript Front-end Vue.js