Как обработать результаты двух асинхронных запросов с условиями success, fail и timeout в JavaScript?
Что проверяют
Умеете ли вы комбинировать промисы под условие задачи и различать три исхода: ответ пришёл, запрос упал, время вышло. Отдельно смотрят, отменяете ли вы запрос по таймауту или просто перестаёте его ждать. Следом часто спрашивают, что изменится, если нужен хотя бы один успешный ответ.
Как отвечать
Сначала уточните условие, от него зависит комбинатор:
Promise.all: нужны оба результата, падение любого сразу отклоняет общий промис;Promise.allSettled: результаты независимы, нужен статус каждого, общий промис не отклоняется никогда;Promise.any: хватит первого успешного, если упали все, придётAggregateError;Promise.race: побеждает первый завершившийся, с любым исходом.
Таймаут делайте через отмену, а не через гонку с setTimeout:
async function load(url, ms) {
const res = await fetch(url, { signal: AbortSignal.timeout(ms) });
if (!res.ok) throw new Error('HTTP ' + res.status);
return res.json();
}
const results = await Promise.allSettled([load('/a', 5000), load('/b', 5000)]);Дальше каждый элемент разбирается по статусу: fulfilled это success, rejected с reason.name === 'TimeoutError' это timeout, остальное fail. AbortSignal.timeout действительно обрывает запрос. Если нужна ещё и ручная отмена, например при размонтировании компонента, сигналы объединяются через AbortSignal.any([controller.signal, AbortSignal.timeout(ms)]), а в React controller.abort() вызывают в функции очистки useEffect.
Где спотыкаются
Делают таймаут через Promise.race с setTimeout. Промис отклонён, но запрос продолжает идти, таймер не очищен, а поздний ответ может перезаписать состояние.
Забывают, что fetch не отклоняется на 404 и 500. Без проверки res.ok ошибка сервера попадёт в success.
Оборачивают Promise.allSettled в try/catch, чтобы поймать упавший запрос. Туда он не попадёт: ошибки лежат внутри результатов, и разбирать их нужно явно.