На днях мой коллега столкнулся с неожиданной ситуацией при использовании встроенного метода forEach типа javascript Array:

«Вы заметили, что await не работает с forEach?», — говорит он.

Хотя это не совсем так, вы действительно можете столкнуться с проблемами при использовании await с forEach, если вы не полностью понимаете концепцию промисов и асинхронных функций, рассмотрите это образец:

Теперь, как вы думаете, что будет напечатано в консоли после запуска этого примера? Вашей первой мыслью может быть: 0, 1, 2, 3, 4, 5, 6 с задержкой в ​​1 секунду между 0 и 4, а затем 5 и 6 примерно в одно и то же время. Но если вы запустите это, вы можете быть удивлены, увидев, что 0 и 6 печатаются первыми, а сразу за ними следуют 1, 2, 3, 4 и 5 через 1 секунду.

Чтобы понять, почему это происходит, нам нужно посмотреть на сигнатуру нашего замыкания forEach:

 async function executor(bar)

Поскольку это асинхронная функция, она возвращает обещание, которое разрешается после завершения работы нашего исполнителя.

Теперь согласно спецификации промис-исполнитель вызывается при построении промиса, то есть все пять исполнителей запускаются одновременно, сразу следуя друг за другом. Каждый из них, однако, останавливается на вызове await и возвращает выполнение основной функции, которая после завершения функции forEach переходит к последней строка, печатающая 6сразу после 0, в то время как все 5 исполнителей наших промисов все еще ждут завершения 1-секундной задержки. Как только эта задержка закончится, в консоли появятся цифры 1–5, и выполнение программы завершится.

Как этого избежать? В зависимости от ваших потребностей вы можете использовать цикл for или for of, если хотите дождаться завершения каждого исполнителя перед запуском следующего, или Array.map в в сочетании с Promise.all, если мы хотим, чтобы они работали параллельно: