Offer to come, dig friends take it! I am participating in the 2022 Spring Recruit Punch card activity. Click here for details.

We sometimes need to iterate over a set of elements and pass them into an asynchronous function, which is prone to errors. Let’s look at some of the errors.

Let’s say we have a sleepPromise asynchronous method of the form:

function sleepPromise(msg, t{
  return new Promise((resolve) = > {
    setTimeout(() = > {
      resolve(`promise-${msg} ${t}s`);
    }, t * 1000); })}Copy the code

Here, for demonstration purposes, we use setTimeout to write a sleep method in the form of a promise. The passed t is the time of delayed execution and MSG is the information content.

In real development, the asynchronous approach might be to pass in the user’s friend ID to look up the database and get simple friend information.

Suppose we need to write an asynchronous convenience implementation below the comment position in the code below.

async function loopAsync({
  console.log('[start]');
  const curTime = Date.now();

  const tasks = [
    ['a'.3],
    ['b'.1],
    ['c'.2]];// Let's iterate over the implementation of the tasks passed in to the asynchronous method

  console.log(`[end] duration The ${((Date.now() - curTime) / 1000).toFixed(2)}s`);
}
Copy the code

Error: forEach

Usually the front end uses forEach when it sees it’s going through a set of numbers. If you’re not sophisticated enough, you might write an implementation like this:

// error forEach
tasks.forEach(async task => {
  const [ msg, t ] = task;
  const m = await sleepPromise(msg, t);
  console.log(m);
})
Copy the code

The output result is;

[start]
[end] duration 0.00s
promise-b 1s
promise-c 2s
promise-a 3s
Copy the code

That’s not the right way to write it, but it’s actually writing the traversal as synchronization.

What’s the problem? ForEach itself does not support async writing, and it does not matter if you prefix a forEach method with or without await keyword because it has no internal logic to handle async writing.

ForEach is an ES5 API that predates ES6 Promise. ForEach will also not support asynchronous processing in the future for backward compatibility.

ForEach execution does not block the code after loopAsync, so the block fails and [end] is printed first.

Serial: for loop

For (const task of tasks) {const [MSG, t] = task; const m = await sleepPromise(msg, t); console.log(m); }Copy the code

With normal for writing, the outer functions of the await are still loopAysnc methods and the blocking code is saved correctly.

The problem here, however, is that these asynchronous methods are executed serially. You can see that 6 s is executed.

[start]
promise-a 3s
promise-b 1s
promise-c 2s
[end] duration 6.01s
Copy the code

This is fine if our requests are sequentially dependent.

However, if our scenario is to find the corresponding user name from the database based on the user ID array, our time complexity is O(n), which is not reasonable.

At this point, we need to rewrite to parallel asynchrony, and ensure that all asynchrony is complete before proceeding to the next step. We can use promise.all ().

Parallel implementation: promise.all

// parallel writing
const taskPromises = tasks.map(task= > {
  const [ msg, t ] = task;
  return sleepPromise(msg, t).then(m= > {
    console.log(m);
  });
});
await Promise.all(taskPromises);
Copy the code

First, we need to generate the corresponding array of promise objects from the Tasks array and pass it into the promise.all method for execution.

In this way, the asynchronous methods are executed simultaneously. When all asynchrony is complete, the code moves forward.

The following output is displayed:

[start]
promise-b 1s
promise-c 2s
promise-a 3s
[end] duration 3.00s
Copy the code

Three seconds. That’s amazing.

Back in the forEach

ForEach is a simple forEach that supports asynchronous processing. ForEach is a simple forEach that supports asynchronous processing.

Parallel implementation:

async function forEach(arr, fn{
  const fns = [];
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    fns.push(fn(item, i, arr));
  }
  await Promise.all(fns);
}
Copy the code

Serial implementation:

async function forEach(arr, fn{
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    awaitfn(item, i, arr); }}Copy the code

Usage:

await forEach(tasks, async task => {
  const [ msg, t ] = task;
  const m = await sleepPromise(msg, t);
  console.log(m);
})
Copy the code

conclusion

Just to summarize.

In general, the asynchronous parallel execution of promise. all is more commonly used, which is common when a database looks up some data corresponding to an ID.

The serial version of the for loop is suitable for multiple asynchronous dependencies, such as finding the final recommender.

ForEach is pure error unless async/await is not required.

I am front-end watermelon brother, focus on sharing front-end knowledge, welcome to follow me.