Как обработать ошибку, если хотя бы один из нескольких запросов не ответил в JavaScript?
Что проверяют
Знаете ли вы разницу между комбинаторами промисов и понимаете ли, что вообще считать ошибкой. В задаче две ловушки: fetch не отклоняется на HTTP-ошибках, а зависший запрос сам по себе ошибкой не станет никогда. Следом часто спрашивают, как отменить оставшиеся запросы.
Как отвечать
Сначала уточните требование: нужны все ответы или достаточно части. От этого зависит комбинатор:
Promise.all: отклоняется на первой же ошибке, остальные результаты теряются. Подходит, когда без любого из ответов экран не имеет смысла;Promise.allSettled: ждёт все промисы и возвращает массив объектов{ status, value }или{ status, reason }. Подходит, когда полезен частичный результат: показать то, что пришло, и отметить упавшие блоки;Promise.any: берёт первый успешный, а если упали все, отклоняется сAggregateError. Удобен для запроса к нескольким зеркалам;Promise.race: первый завершившийся в любую сторону, классический способ приделать таймаут.
Дальше две обязательные детали. Первая: fetch отклоняет промис только при сетевой ошибке или отмене, ответы 404 и 500 приходят успешно, поэтому каждую обёртку проверяют через if (!res.ok) throw .... Вторая: «не ответил» означает таймаут. Его задают через AbortSignal.timeout(5000) в опциях fetch, а с общим контроллером объединяют через AbortSignal.any.
С Promise.all после первой ошибки остальные запросы продолжают выполняться: промисы не отменяются. Хороший ответ показывает общий AbortController, у которого в catch вызывается abort(), чтобы не тратить сеть впустую. Повторы при сбое добавляют с экспоненциальной задержкой и только для идемпотентных запросов.
Где спотыкаются
Оборачивают Promise.all в try/catch и считают, что обработали ошибку каждого запроса. На деле ловится только первая, о судьбе остальных код не узнает.
Забывают про res.ok. Сервер вернул 500, а код радостно разбирает JSON с текстом ошибки как данные.
Путают Promise.race с Promise.any. race завершится первым же отказом, даже если через миллисекунду пришёл бы успешный ответ.