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); }
Использование в таком виде даст желаемый результат.