Ожидает ли функция Array.prototype.forEach() только синхронную функцию?

Глядя на код ниже:

async function displayScores() {
  const scores = [1, 2, 3, 4, 5];
  console.log("Starting to display scores");
  scores.forEach((score) => {
    console.log('The current score is ', score);
  });
  console.log("Finished displaying scores. This should execute last!");
}
displayScores();

Этот код даст следующий результат:

Starting to display scores
The current score is 1
The current score is 2
The current score is 3
The current score is 4
The current score is 5
Finished displaying scores. This should execute last!

Однако изменение кода с помощью некоторой асинхронной функции:

function myFunction(value) {
  return new Promise((resolve) => {
    console.log("My extra function is running");
    resolve();
  });
}
async function displayScores() {
  const scores = [1, 2, 3, 4, 5];
  console.log("Starting to display scores");
  scores.forEach(async (score) => {
    console.log('Before the async function:');
    await myFunction()
    console.log('The current score is ', score);
  });
  console.log("Finished displaying scores. This should execute last!");
}
displayScores();

Вы ожидаете, что Finished отобразит баллы. Это должно выполняться последним! по-прежнему печататься последним, поскольку forEach является синхронным.

На самом деле это вывод: 🤯

Starting to display scores
Before the async function:
My extra function is running
Before the async function:
My extra function is running
Before the async function:
My extra function is running
Before the async function:
My extra function is running
Before the async function:
My extra function is running
Finished displaying scores. This should execute last!
The current score is  1
The current score is  2
The current score is  3
The current score is  4
The current score is  5

Я узнал об этом, когда это вызвало ошибку в веб-приложении, над которым я работал.

Я понял, что использование async/await с forEach приводит к неожиданному поведению. Хотя кажется, что ключевое слово await выполняет свою работу, цикл forEach не ожидает обещания, когда он переходит к следующему циклу.

Мое понимание неожиданного поведения может быть недостаточно ясным, постарайтесь прокомментировать свои мысли по этому поводу.

Несмотря на это, я обнаружил, что это решение работает.

Прибегнуть к использованию базового цикла for, for(;;;) и for…of — отлично сработали.

Еще одно работающее решение — использование Array.map вместе с Promise.all() как таковое:

await Promise.all(
    scores.map(async (score) => {
        console.log('Before the async function:');
        await myFunction()
        console.log('The current score is ', score);
    })
);
// This works perfectly.
OUTPUT:
Starting to display scores
Before the async function:
My extra function is running
Before the async function:
My extra function is running
Before the async function:
My extra function is running
Before the async function:
My extra function is running
Before the async function:
My extra function is running
The current score is  1
The current score is  2
The current score is  3
The current score is  4
The current score is  5
Finished displaying scores. This should execute last!

Поделитесь своими мыслями и опытом в разделе комментариев.

Спасибо! 👍

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter и LinkedIn. Посетите наш Community Discord и присоединитесь к нашему Коллективу талантов.