Как записывать ошибки в массив по индексу, соответствующему индексу промиса в JavaScript?
Что проверяют
Понимаете ли вы, что промисы завершаются в произвольном порядке и привязка к индексу должна идти от исходного массива, а не от момента завершения. Заодно проверяют знание Promise.allSettled и поведения Promise.all при ошибке. Следом часто просят то же самое с ограничением параллельности.
Как отвечать
Самый прямой способ: Promise.allSettled (ES2020). Он ждёт все промисы и возвращает результаты в порядке входного массива, независимо от того, кто завершился первым:
const results = await Promise.allSettled(tasks)
const errors = results.map(r => r.status === 'rejected' ? r.reason : null)Второй способ, когда нужен контроль внутри цепочки: повесить catch на каждый промис и писать по индексу из замыкания.
const errors = new Array(tasks.length).fill(null)
await Promise.all(tasks.map((p, i) => p.catch(e => { errors[i] = e })))Здесь Promise.all уже не отклонится, потому что каждая ошибка перехвачена.
Что стоит проговорить:
- порядок: индекс берётся из
map, поэтому место ошибки совпадает с местом промиса, даже если он упал последним; Promise.all: без перехвата отклоняется на первой ошибке, а остальные промисы продолжают выполняться, их результаты просто теряются;- успехи: удобно собирать результаты и ошибки одним проходом, в два массива одной длины или в массив объектов;
- параллельность: промис начинает работу в момент создания, поэтому для лимита на вход подают функции. Пул из N воркеров берёт следующий индекс из общего счётчика и пишет итог в
results[i].
Где спотыкаются
Используют errors.push(e). Ошибки ложатся в порядке завершения, и связь с исходным промисом теряется.
Берут r.reason.message не глядя. Отклонить промис можно чем угодно, включая строку или undefined, и тогда сам обработчик ошибок упадёт с TypeError. Храните reason целиком или проверяйте instanceof Error.
Пишут tasks.forEach(async (p, i) => ...) и сразу читают массив ошибок. forEach не ждёт асинхронных колбэков, и в этот момент массив ещё пустой.