Как определить момент завершения всех промисов с ошибками для вызова reject в JavaScript?
Что проверяют
Знаете ли вы комбинаторы промисов и их поведение на отказах, а не только Promise.all. Часто просят написать решение без встроенных методов, и тогда смотрят, как вы сохраняете порядок результатов и обрабатываете пустой массив.
Как отвечать
Сначала уточните задачу: нужно дождаться всех промисов или достаточно первого успеха.
Promise.any(ES2021): резолвится первым успешным значением и отклоняется, только когда отклонены все. Причина отказа этоAggregateError, в его полеerrorsлежат все причины в порядке исходного массива. Если нужен ровно «reject, когда упали все», это готовый ответ;Promise.allSettled(ES2020): ждёт всех и никогда не отклоняется, возвращает массив объектов{ status, value }или{ status, reason }. Подходит, если нужны все результаты, а отклонить итог хочется только при полном провале: проверяетеresults.every(r => r.status === 'rejected')и бросаетеnew AggregateError(reasons, 'all failed');Promise.all: отклоняется на первой ошибке, а остальные промисы продолжают выполняться, их никто не отменяет;Promise.race: первый завершившийся, успех или ошибка.
Ручная реализация: счётчик отказов, массив причин с записью по индексу (не push, иначе порядок будет по времени), каждый элемент оборачивается в Promise.resolve(), чтобы принимать и обычные значения. Когда счётчик равен длине массива, вызываете reject, на первом успехе resolve.
Где спотыкаются
Забывают про пустой массив. Promise.any([]) сразу отклоняется, а самописная проверка «ошибок столько же, сколько промисов» на пустом входе даёт 0 === 0 и отклоняет там, где, возможно, ожидался пустой успех.
Отклоняют итог строкой «все упали» и выбрасывают исходные причины. Отладка превращается в гадание, а AggregateError сохранил бы их все.
Думают, что Promise.all отменяет остальные запросы. Для отмены нужен AbortController, сигнал которого передают в каждый fetch.