Проанализируйте следующий JavaScript-асинхронный кейс: "arr.forEach(async x => await doSomething(x)); console.log('done');" — что выполняется первым и почему, как правильно дождаться завершения всех операций
Что выполняется первым и почему - В приведённом коде сразу после вызова `arr.forEach(async x => await doSomething(x));` синхронно выполняется `console.log('done')`. - Причина: `forEach` не учитывает возвращаемые промисы — он вызывает асинхронные колбэки синхронно и не ждёт их завершения. Каждый `async`-колбэк возвращает `Promise`, но эти промисы просто игнорируются, поэтому основной поток продолжает выполнение и выводит `done` раньше, чем завершатся `doSomething`. Как правильно дождаться завершения всех операций - Параллельно (выполнить все одновременно и ждать всех): - Собрать массив промисов и `await Promise.all(...)`. - Пример: async function runAll(arr) { await Promise.all(arr.map(x => doSomething(x))); console.log('done'); } - Здесь `Promise.all` ждёт все промисы (их количество N=arr.lengthN = arr.lengthN=arr.length). Учтите, что `Promise.all` отклонится при первой же ошибке; если нужно дождаться результатов всех независимо от ошибок — используйте `Promise.allSettled`. - Последовательно (по одному, ждать каждого перед переходом к следующему): - Использовать `for...of` с `await`: async function runSequential(arr) { for (const x of arr) { await doSomething(x); } console.log('done'); } Коротко про часто встречающуюся ошибку - `arr.forEach(async ...)` НЕ создаёт промис, который можно `await`. Для ожидания нужен явный массив промисов (например через `map`) или последовательный `for...of`.
- В приведённом коде сразу после вызова `arr.forEach(async x => await doSomething(x));` синхронно выполняется `console.log('done')`.
- Причина: `forEach` не учитывает возвращаемые промисы — он вызывает асинхронные колбэки синхронно и не ждёт их завершения. Каждый `async`-колбэк возвращает `Promise`, но эти промисы просто игнорируются, поэтому основной поток продолжает выполнение и выводит `done` раньше, чем завершатся `doSomething`.
Как правильно дождаться завершения всех операций
- Параллельно (выполнить все одновременно и ждать всех):
- Собрать массив промисов и `await Promise.all(...)`.
- Пример:
async function runAll(arr) {
await Promise.all(arr.map(x => doSomething(x)));
console.log('done');
}
- Здесь `Promise.all` ждёт все промисы (их количество N=arr.lengthN = arr.lengthN=arr.length). Учтите, что `Promise.all` отклонится при первой же ошибке; если нужно дождаться результатов всех независимо от ошибок — используйте `Promise.allSettled`.
- Последовательно (по одному, ждать каждого перед переходом к следующему):
- Использовать `for...of` с `await`:
async function runSequential(arr) {
for (const x of arr) {
await doSomething(x);
}
console.log('done');
}
Коротко про часто встречающуюся ошибку
- `arr.forEach(async ...)` НЕ создаёт промис, который можно `await`. Для ожидания нужен явный массив промисов (например через `map`) или последовательный `for...of`.