Translator: Front-end wisdom

Medium.com/free-code-c…

Click “like” and then look, wechat search [Big Move the world] pay attention to this person without dACHang background, but with a positive attitude upward. In this paper, making github.com/qq449245884… Has been included, the article has been categorized, also organized a lot of my documentation, and tutorial materials.

Everyone said there was no project on your resume, so I found one and gave it away【 Construction tutorial 】.

In this article, I share some noteworthy issues with using await in loops.

Prepare an example

For this article, suppose you want to get the number of fruits from a fruit basket.

const fruitBasket = {
 apple: 27,
 grape: 0,
 pear: 14
};
Copy the code

You want to get the amount of each fruit from fruitBasket. To get the number of fruits, use the getNumFruit function.

const getNumFruit = fruit => { return fruitBasket[fruit]; }; const numApples = getNumFruit('apple'); console.log(numApples); / / 27Copy the code

Now, assume that the fruitBasket is fetched from the server, which we simulate using setTimeout.

const sleep = ms => { return new Promise(resolve => setTimeout(resolve, ms)) }; const getNumFruit = fruit => { return sleep(1000).then(v => fruitBasket[fruit]); }; getNumFruit("apple").then(num => console.log(num)); / / 27Copy the code

Finally, suppose you want to get the number of each fruit in an asynchronous function using await and getNumFruit.

const control = async _ => {
  console.log('Start')

  const numApples = await getNumFruit('apple');
  console.log(numApples);

  const numGrapes = await getNumFruit('grape');
  console.log(numGrapes);

  const numPears = await getNumFruit('pear');
  console.log(numPears);

  console.log('End')
}
Copy the code

Use await in the for loop

First we define an array to hold the fruit:

Const fruitsToGet = [" apple ", "grape", "pear"];Copy the code

Loop through the array:

const forLoop = async _ => { console.log('Start'); for (let index = 0; index < fruitsToGet.length; Index++) {// get the number of fruits per fruit} console.log('End')}Copy the code

In the for loop, use getNumFruit to get the number of each fruit and print the number to the console.

Since getNumFruit returns a promise, we use await to wait for the result to return and print it.

const forLoop = async _ => {
  console.log('start');

  for (let index = 0; index < fruitsToGet.length; index ++) {
    const fruit = fruitsToGet[index];
    const numFruit = await getNumFruit(fruit);
    console.log(numFruit);
  }
  console.log('End')
}
Copy the code

When you use await, you want JavaScript to pause execution until you wait for a promise to return processing results. This means that await in the for loop should be executed sequentially.

The result is as you would expect.

"Start"; "Apple: 27"; "Grape: 0"; "The Pear: 14"; "The End";Copy the code

This behavior applies to most loops (such as while and for-of loops)…

But it does not handle loops that require callbacks, such as forEach, Map, Filter, and Reduce. In the next few sections, we’ll look at how await affects forEach, Map, and filter.

Use await in forEach loop

First, the array is iterated through using forEach.

const forEach = _ => {
  console.log('start');

  fruitsToGet.forEach(fruit => {
    //...
  })

  console.log('End')
}
Copy the code

Next, we’ll try using getNumFruit to get the number of fruits. Notice the async keyword in the callback function. We need the async keyword because await is in the callback function).

const forEachLoop = _ => {
  console.log('Start');

  fruitsToGet.forEach(async fruit => {
    const numFruit = await getNumFruit(fruit);
    console.log(numFruit)
  });

  console.log('End')
}
Copy the code

I expect the console to print the following:

"Start"; "27"; "0"; "14"; "The End";Copy the code

But the actual results are different. JavaScrip executes console.log(‘End’) before waiting for the result to return in the forEach loop.

The actual console print is as follows:

'Start' 'End' '27' '0' '14'Copy the code

ForEach in JavaScript does not support promise awareness and does not support async and await, so you cannot use await in forEach.

Use await in map

If you use await in a map, the map always returns a Promise array, because asynchronous functions always return promises.

const mapLoop = async _ => { console.log('Start') const numFruits = await fruitsToGet.map(async fruit => { const numFruit = await getNumFruit(fruit); return numFruit; }) console.log(numFruits); The console. The log (' End ')} "Start"; "[Promise, the Promise, the Promise]"; "The End";Copy the code

If you use await in map, map always returns Promises, you have to wait for Promises array to be processed. Or do this by await promise. all(arrayOfPromises).

const mapLoop = async _ => {
  console.log('Start');

  const promises = fruitsToGet.map(async fruit => {
    const numFruit = await getNumFruit(fruit);
    return numFruit;
  });

  const numFruits = await Promise.all(promises);
  console.log(numFruits);

  console.log('End')
}
Copy the code

The running results are as follows:

If you wish, you can handle the return value in the Promise, and the parsed value will be the returned value.

const mapLoop = _ => { // ... const promises = fruitsToGet.map(async fruit => { const numFruit = await getNumFruit(fruit); return numFruit + 100 }) // ... } "Start"; "[127, 100, 114]." "The End";Copy the code

Use await in filter loop

When you use filter, you want to filter an array with a particular result. Assume that the number of arrays filtered is greater than 20.

If you normally use filter (without await), it would look like this:

const filterLoop =  _ => {
  console.log('Start')

  const moreThan20 =  fruitsToGet.filter(async fruit => {
    const numFruit = await fruitBasket[fruit]
    return numFruit > 20
  })
  
  console.log(moreThan20) 
  console.log('END')
}
Copy the code

The results

Start
["apple"]
END
Copy the code

Await in filter does not work in the same way. In fact, it doesn’t work at all.

const filterLoop = async _ => { console.log('Start') const moreThan20 = await fruitsToGet.filter(async fruit => { const NumFruit = fruitBasket[fruit] return numFruit > 20}) console.log(moreThan20) console.log('END') ["apple", "grape", "pear"] ENDCopy the code

Why does this happen?

When await is used in a filter callback, the callback is always a promise. Since promises are always true, all items in the array pass through filter. Use the code below the await class in filter

const filtered = array.filter(true);
Copy the code

The correct three steps to use await in filter

  1. Use map to return a Promise array

  2. Await the result of processing with await

  3. Use filter to process the result returned

    const filterLoop = async _ => { console.log(‘Start’);

    const promises = await fruitsToGet.map(fruit => getNumFruit(fruit));

    const numFruits = await Promise.all(promises);

    const moreThan20 = fruitsToGet.filter((fruit, index) => { const numFruit = numFruits[index]; return numFruit > 20; })

    console.log(moreThan20); console.log(‘End’) }

Use await in reduce loop

If you want to count the total number of fruits in fruitBastet. In general, you can use the Reduce loop to iterate through a set of numbers and add the numbers.

const reduceLoop = _ => {
  console.log('Start');

  const sum = fruitsToGet.reduce((sum, fruit) => {
    const numFruit = fruitBasket[fruit];
    return sum + numFruit;
  }, 0)

  console.log(sum)
  console.log('End')
}
 
Copy the code

Running results:

When you use await in reduce, the result becomes very confusing.

 const reduceLoop = async _ => {
  console.log('Start');

  const sum = await fruitsToGet.reduce(async (sum, fruit) => {
    const numFruit = await fruitBasket[fruit];
    return sum + numFruit;
  }, 0)

  console.log(sum)
  console.log('End')
}
 
Copy the code

What the hell is 14?

It’s interesting to dissect that.

  1. In the first iteration, sum is 0. NumFruit is 27(the value obtained from getNumFruit(apple)), 0 + 27 = 27.

  2. In the second iteration, sum is a promise. B: Why? Because asynchronous functions always return Promises! NumFruit is 0. A promise cannot be added to an object properly, so JavaScript converts it to an [Object Promise] string. [Object Promise] + 0 is object Promise] 0.

  3. On the third iteration, sum is also a promise. NumFruit is 14. [object Promise] + 14 is [Object Promise] 14.

Solve the mystery!

This means that you can use await in reduce callbacks, but you must remember to wait for the accumulator first!

const reduceLoop = async _ => {
  console.log('Start');

  const sum = await fruitsToGet.reduce(async (promisedSum, fruit) => {
    const sum = await promisedSum;
    const numFruit = await fruitBasket[fruit];
    return sum + numFruit;
  }, 0)

  console.log(sum)
  console.log('End')
}
Copy the code

But as you can see in the figure above, await operations take a long time. This happens because the reduceLoop waits for each iteration to complete the promisedSum.

One way to speed up the Reduce cycle is to reduce oop in a second if you wait for getNumFruits() before waiting for promisedSum:

const reduceLoop = async _ => {
  console.log('Start');

  const sum = await fruitsToGet.reduce(async (promisedSum, fruit) => {
    const numFruit = await fruitBasket[fruit];
    const sum = await promisedSum;
    return sum + numFruit;
  }, 0)

  console.log(sum)
  console.log('End')
}
Copy the code

This is because Reduce can trigger all three getNumFruit Promises before waiting for the next iteration of the loop. However, this method is a bit confusing because you have to pay attention to the order in which you wait.

The simplest (and most efficient) way to use Wait in Reduce is

  1. Use map to return a Promise array

  2. Await the result of processing with await

  3. The returned results are processed using reduce

    const reduceLoop = async _ => { console.log(‘Start’);

    const promises = fruitsToGet.map(getNumFruit); const numFruits = await Promise.all(promises); const sum = numFruits.reduce((sum, fruit) => sum + fruit);

    console.log(sum) console.log(‘End’) }

This version is easy to read and understand and takes a second to count the fruit.

What do you see up there

  1. If you want to continuously perform await calls, use the for loop (or any loop without callbacks).

  2. Never use await with forEach, instead use for loops (or any loops without callbacks).

  3. Do not use await in Filter and reduce. If necessary, further processing with Map and then processing with Filter and reduce.

communication

This article is updated every week, you can search wechat “big move the world” for the first time to read and urge more (one or two earlier than the blog hey), this article GitHub github.com/qq449245884… It has been included and sorted out a lot of my documents. Welcome Star and perfect. You can refer to the examination points for review in the interview.