> Как реализовать таймаут с abort для запроса в JavaScript (JavaScript)
Уровень: senior · Роль: frontend · Категория: Технические вопросы
Компании: циан
Стек: JavaScript
> Пример ответа
Короткий ответ
Таймаут с abort для запроса в JavaScript реализуется через комбинацию AbortController и setTimeout. Создаётся контроллер, его signal передаётся в fetch, а таймер вызывает abort() по истечении лимита. В catch обрабатывается ошибка AbortError отдельно от сетевых ошибок. Для повторного использования удобно обернуть логику в функцию или использовать AbortSignal.timeout() в современных браузерах.
Подробное объяснение
AbortController - встроенный механизм для отмены асинхронных операций, поддерживаемый fetch, axios (через signal), чтением потоков и другими API. При вызове abort():
signal.abortedстановитсяtrue- генерируется событие
abortнаsignal fetchотклоняет промис с ошибкойDOMExceptionтипаAbortError
Таймаут - это просто setTimeout, который вызывает abort() через заданный интервал. Важно очищать таймер после завершения запроса, чтобы избежать утечек и лишних вызовов.
AbortSignal.timeout(ms) - статический метод, создающий сигнал, который автоматически прерывается через указанное время. Это сокращает код, но не позволяет отменить запрос вручную (например, по клику пользователя) - для этого нужен отдельный контроллер.
Комбинированный подход: можно объединить внешний AbortController и AbortSignal.timeout через AbortSignal.any() (современные браузеры), чтобы поддерживать и ручную отмену, и таймаут.
На практике
Основной сценарий - защита от "зависших" запросов: пользователь ушёл со страницы, сеть недоступна, сервер не отвечает. Таймаут с abort позволяет освободить ресурсы и показать внятную ошибку.
При реализации важно:
- всегда очищать таймер в
finally, иначе он сработает после завершения запроса - проверять
error.name === 'AbortError', чтобы отличить таймаут от других ошибок - не использовать
AbortSignal.timeout()без внешнего контроллера, если нужна отмена по действию пользователя - учитывать, что
abort()не отменяет сам HTTP-запрос на уровне сети - он лишь отклоняет промис и закрывает соединение (в большинстве браузеров)
Пример кода
JAVASCRIPTasync function fetchWithTimeout(url, options = {}, timeoutMs = 5000) {const controller = new AbortController();const { signal } = controller;const timeoutId = setTimeout(() => controller.abort(), timeoutMs);try {const response = await fetch(url, { ...options, signal });return await response.json();} catch (error) {if (error.name === 'AbortError') {throw new Error(`Request timed out after ${timeoutMs}ms`);}throw error;} finally {clearTimeout(timeoutId);}}// использованиеtry {const data = await fetchWithTimeout('/api/data', { method: 'GET' }, 3000);console.log(data);} catch (error) {console.error(error.message);}
Современный вариант с AbortSignal.timeout:
JAVASCRIPTasync function fetchWithTimeoutModern(url, timeoutMs = 5000) {try {const response = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });return await response.json();} catch (error) {if (error.name === 'TimeoutError') {throw new Error(`Request timed out after ${timeoutMs}ms`);}throw error;}}
Как отвечать на собеседовании
Начни с AbortController как основного механизма, затем добавь setTimeout для таймаута. Покажи понимание жизненного цикла: создание контроллера, передача signal, очистка таймера. Упомяни AbortSignal.timeout() как современную альтернативу, но сразу отметь её ограничение - невозможность ручной отмены. Если спросят про AbortSignal.any() - расскажи про комбинирование сигналов. Подчеркни важность обработки AbortError отдельно от других ошибок.
Что проверяет интервьюер
- знание встроенных API браузера для отмены асинхронных операций
- умение корректно управлять ресурсами (очистка таймера)
- понимание различий между типами ошибок (
AbortError,TimeoutError, сетевые ошибки) - способность предложить практичное решение с учётом edge cases (ручная отмена + таймаут)
- актуальность знаний (знакомство с современными методами
AbortSignal)
Типичные ошибки
- не очищать таймер - приводит к срабатыванию
abort()после успешного ответа - игнорировать проверку
error.name- таймаут будет обработан как обычная сетевая ошибка - использовать
AbortSignal.timeout()там, где нужна ручная отмена - забывать, что
abort()не гарантирует отмену на серверной стороне - запрос может дойти - оборачивать
fetchвPromise.raceс таймером - это не отменяет запрос, а лишь игнорирует результат (утечка ресурсов)
> Похожие задачи по frontend
Как добавить новый метод в стандартный объект Array через prototype в JavaScript
Можно ли создать функцию для запроса продуктов, которая возвращает промис
Как реализовать функцию promisify в JavaScript
В чем разница каррирования и частичного применения в JavaScript
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью