> Как реализовать таймаут с 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-запрос на уровне сети - он лишь отклоняет промис и закрывает соединение (в большинстве браузеров)

Пример кода

JAVASCRIPT
async 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:

JAVASCRIPT
async 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 с таймером - это не отменяет запрос, а лишь игнорирует результат (утечка ресурсов)

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

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