> Как обработать результаты двух асинхронных запросов с условиями success, fail и timeout в JavaScript? (JavaScript)
Уровень: senior · Роль: frontend · Категория: Технические вопросы
Компании: Яндекс
Стек: JavaScript
> Пример ответа
Короткий ответ
Для обработки двух асинхронных запросов с условиями success, fail и timeout используйте Promise.allSettled с AbortController для таймаута или Promise.race с отдельным таймером. Для каждого запроса создайте отдельный промис с обработкой ошибок и таймаутом. Затем объедините результаты через Promise.all с маппингом статусов. Это даёт контроль над каждым сценарием без потери данных при частичном отказе.
Подробное объяснение
Основная сложность - одновременная обработка трёх состояний для каждого запроса: успех, ошибка и превышение времени ожидания. В JavaScript нет встроенного механизма для таймаутов в fetch или XMLHttpRequest, поэтому нужно реализовать его вручную.
Ключевые подходы:
AbortController- современный способ отмены запросов и установки таймаута черезsignal. Позволяет явно прервать запрос и обработать отмену как отдельный кейс.Promise.race- классический метод, где вы запускаете запрос и таймер-промис, который отклоняется через заданное время. Минус - запрос продолжает выполняться в фоне.Promise.allSettled- для сбора результатов обоих запросов независимо от их статуса, что удобно при частичных отказах.
Для каждого запроса создаётся обёртка, которая возвращает промис с тремя возможными исходами: resolved (success), rejected (fail) или rejected с признаком timeout. Затем оба промиса запускаются параллельно, и результаты агрегируются.
Важно различать сетевую ошибку и таймаут - это разные сценарии fail. Таймаут обычно обрабатывается как отдельный тип ошибки, чтобы интерфейс мог показать соответствующее сообщение.
На практике
В реальных проектах (например, React-приложениях) такая логика часто инкапсулируется в кастомный хук или сервисный слой. Типичный сценарий - загрузка данных из двух API, где каждый запрос может упасть по таймауту, и нужно показать частичный результат или повторить только упавший запрос.
Используйте AbortController для каждого запроса отдельно, чтобы таймаут одного не отменял другой. Для обработки результатов удобно создать объект-маппер, который преобразует статусы в единый формат для UI.
Пример кода
JAVASCRIPTfunction fetchWithTimeout(url, timeout = 5000) {const controller = new AbortController();const timeoutId = setTimeout(() => controller.abort(), timeout);return fetch(url, { signal: controller.signal }).then(response => {clearTimeout(timeoutId);if (!response.ok) throw new Error(`HTTP ${response.status}`);return response.json();}).catch(error => {clearTimeout(timeoutId);if (error.name === 'AbortError') {throw { type: 'timeout', message: 'Request timed out' };}throw { type: 'fail', message: error.message };});}async function handleTwoRequests(url1, url2, timeout = 5000) {const results = await Promise.allSettled([fetchWithTimeout(url1, timeout),fetchWithTimeout(url2, timeout)]);return results.map(result => {if (result.status === 'fulfilled') {return { status: 'success', data: result.value };}// result.reason содержит { type, message }return {status: result.reason.type === 'timeout' ? 'timeout' : 'fail',error: result.reason.message};});}// ИспользованиеhandleTwoRequests('/api/data1', '/api/data2', 3000).then(results => {results.forEach((r, i) => {if (r.status === 'success') console.log(`Request ${i+1}:`, r.data);else console.warn(`Request ${i+1}: ${r.status} - ${r.error}`);});});
Как отвечать на собеседовании
Начните с упоминания AbortController как современного стандарта для таймаутов. Объясните, почему Promise.all не подходит - он падает при первой ошибке, а вам нужна обработка каждого запроса отдельно. Упомяните Promise.allSettled для сбора всех результатов. Покажите понимание разницы между сетевой ошибкой и таймаутом. Если спросят про старые браузеры, добавьте вариант с Promise.race и setTimeout.
Что проверяет интервьюер
- Понимание асинхронности и управления промисами.
- Знание современных API (AbortController, Promise.allSettled).
- Умение проектировать обработку ошибок с разными типами.
- Способность комбинировать несколько асинхронных потоков.
- Внимание к деталям: очистка таймеров, различение типов ошибок.
Типичные ошибки
- Использование
Promise.allбез учёта, что один отказ убивает весь результат. - Забывают очистить таймер после успешного ответа - утечка памяти.
- Не различают таймаут и ошибку сети, обрабатывая их одинаково.
- Используют один
AbortControllerдля обоих запросов - отмена одного отменяет другой. - Пытаются использовать
async/awaitбезtry/catchдля каждого запроса, теряя контроль над отдельными ошибками.
> Похожие задачи по frontend
Как реализовать метод перезагрузки счетчика вызовов функции в декораторе JavaScript
Как обрабатывать превышение лимита вызовов функции в JavaScript и что возвращать
Как обработать ошибку, если хотя бы один из нескольких запросов не ответил в JavaScript?
Как обрабатывать массив результатов промисов в JavaScript и выводить success или fail в консоль
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью