JavaScript Async/Await использование вместе с ForEach

Использование async/await вместе с forEach является довольно распространенной ошибкой. Рассмотрим это на примере простой асинхронной функции, которая выводит что-то в консоль:

async function asyncPrint(val) {
  ...
}
const values = [1,2,3];
values.forEach(async (val) => {
  await asyncPrint(val);
});

Возможно мы будем ожидать, что в консоли будет выведено все в следующем порядке:

1
2
3

Но это далеко не так. Работает это все не так.

Чтобы более явно показать, в чем заключается проблема, мы можем добавить вывод с рандомной задержкой:

async function someAsyncFn(value) {
  const randomDelay = Math.random() * (100 - 10) + 10;
  setTimeout(() => console.log(value), randomDelay);
}
const values = [1, 2, 3];
values.forEach(async (val) => {
  await someAsyncFn(val);
});

И если мы запустим это код несколько раз, то увидим, что вывод происходит каждый раз в случайном порядке:

Так в чем же основная проблема?

forEach ожидает синхронную функцию (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach). И логично предположить, что никакой асинхронной функции не ожидается. Но если же мы все равно ожидаем иное, то это будет эквивалентно этому:

await asyncPrint(1);
await asyncPrint(2);
await asyncPrint(3);

Но так как мы теперь знаем, что forEach ждет именно синхронную операцию, то это будет работать так:

asyncPrint(1);
asyncPrint(2);
asyncPrint(3);

Что делать, если мне нужна асинхронная операция в цикле?

Если вам все же нужно выполнение асинхронной функции в цикле, в таком случае можете воспользоваться циклом for...of:

for(let val of values) {
  await asyncPrint(val);
}

Использование в таком виде даст желаемый результат.


Вы можете поделиться этой статьей в любой из соцсетей, представленных ниже:


Чтобы добавить свой комментарий, необходимо пройти аутентификацию
Комментарии
Ничего не найдено.