> Как обработать ошибку, если хотя бы один из нескольких запросов не ответил в JavaScript? (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Яндекс

Стек: JavaScript

> Пример ответа

Короткий ответ

Для обработки ситуации, когда хотя бы один из нескольких запросов не ответил, используйте Promise.allSettled() - он ждёт завершения всех промисов и возвращает массив с результатами каждого (fulfilled/rejected). Если нужна реакция на первую ошибку - Promise.any() (ждёт первого успешного) или Promise.all() с catch на уровне каждого запроса. Для таймаутов комбинируйте с AbortController и Promise.race().

Подробное объяснение

В JavaScript параллельные запросы обрабатываются через комбинации методов Promise. Основная проблема - гарантировать реакцию на ошибку, не блокируя остальные запросы.

  • Promise.all() - отклоняется при первой ошибке, остальные результаты теряются. Не подходит, если нужно дождаться всех.
  • Promise.allSettled() - ждёт завершения всех, возвращает массив объектов {status, value/reason}. Позволяет обработать каждую ошибку индивидуально.
  • Promise.any() - возвращает первый успешный результат, игнорирует ошибки, пока все не отклонятся (тогда AggregateError).
  • Promise.race() - реагирует на первый завершённый (успех или ошибка). Используется для таймаутов.

Для таймаутов на каждый запрос применяйте AbortController - это стандартный механизм отмены fetch. Комбинируйте с Promise.race(): создаёте промис, который отклоняется через заданное время, и гоняете его с реальным запросом.

На практике

Выбор метода зависит от сценария:

  • Если нужно дождаться всех ответов и обработать каждую ошибку - Promise.allSettled().
  • Если достаточно первого успешного ответа (например, несколько CDN) - Promise.any().
  • Если критично получить все данные, но при таймауте одного запроса нужно отменить остальные - Promise.all() с индивидуальными таймаутами через AbortController.

Для таймаутов используйте AbortSignal.timeout() (современный браузер) или создавайте свой сигнал с AbortController. Не забывайте очищать таймеры в finally.

Пример кода

JAVASCRIPT
async function fetchWithTimeout(url, timeoutMs = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
try {
const response = await fetch(url, { signal: controller.signal });
return response.json();
} finally {
clearTimeout(timeoutId);
}
}
async function handleMultipleRequests(urls) {
const results = await Promise.allSettled(
urls.map(url => fetchWithTimeout(url))
);
const errors = results.filter(r => r.status === 'rejected');
if (errors.length > 0) {
console.error(`Не ответили ${errors.length} запросов:`,
errors.map(e => e.reason));
// Можно выбросить агрегированную ошибку или продолжить
}
return results
.filter(r => r.status === 'fulfilled')
.map(r => r.value);
}

Как отвечать на собеседовании

Начните с ключевого метода Promise.allSettled() как основного решения. Объясните trade-off между Promise.all() (быстрая ошибка, потеря данных) и Promise.allSettled() (полные данные, ручная обработка ошибок). Упомяните AbortController для таймаутов - это показывает понимание современных API. Если спросят про отмену всех запросов при ошибке одного - расскажите про комбинацию AbortController и Promise.all() с общим сигналом. Не углубляйтесь в полифиллы для старых браузеров, если не спросят.

Что проверяет интервьюер

  • Понимание асинхронности и Promise API на уровне senior.
  • Умение выбирать правильный инструмент под задачу (allSettled vs all vs any).
  • Знание механизмов отмены запросов (AbortController).
  • Способность проектировать отказоустойчивые системы с обработкой частичных ошибок.
  • Понимание trade-off между производительностью и надёжностью.

Типичные ошибки

  • Использование Promise.all() без обработки ошибок - при первом reject теряются все остальные результаты.
  • Забывают про таймауты - запрос может висеть бесконечно.
  • Путают Promise.any() и Promise.race() - первый ждёт успеха, второй реагирует на любой исход.
  • Не очищают таймеры в finally при использовании setTimeout с AbortController.
  • Пытаются обработать ошибки через .catch() на каждом запросе, но не агрегируют результаты - теряют контекст.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью