← Назад к вопросам
Зачем нужен AbortController в Node.js?
1.3 Junior🔥 71 комментариев
#Node.js и JavaScript
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Зачем нужен AbortController в Node.js?
AbortController — это API для отмены асинхронных операций. Он позволяет остановить выполнение длительных операций (fetch, timers, streams), не дожидаясь их завершения.
Проблема, которую решает AbortController
Без AbortController (ПЛОХО):
async function downloadFile(url: string) {
const response = await fetch(url); // Может загружаться часами
const data = await response.arrayBuffer();
return data;
}
// Пользователь отменил запрос
// Но загрузка продолжает идти в фоне!
// Пустая трата трафика и памяти
С AbortController (ХОРОШО):
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort(); // Отменить загрузку после 10 сек
}, 10000);
try {
const response = await fetch(url, {
signal: controller.signal
});
const data = await response.arrayBuffer();
return data;
} catch (err) {
if (err.name === 'AbortError') {
console.log('Загрузка отменена');
}
} finally {
clearTimeout(timeoutId);
}
Как работает AbortController?
// 1. Создаём контроллер
const controller = new AbortController();
// 2. Получаем сигнал для передачи в операцию
const signal = controller.signal;
// 3. Проверяем, отменена ли операция
console.log(signal.aborted); // false
// 4. Отменяем
controller.abort();
// 5. После отмены
console.log(signal.aborted); // true
// 6. Вызывается событие abort
signal.addEventListener('abort', () => {
console.log('Операция отменена!');
});
controller.abort(); // Логирует: Операция отменена!
Практические примеры
Пример 1: Timeout для fetch запроса
async function fetchWithTimeout(url: string, timeout: number = 5000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
}, timeout);
try {
const response = await fetch(url, {
signal: controller.signal
});
return await response.json();
} catch (err) {
if (err.name === 'AbortError') {
throw new Error(`Request timeout after ${timeout}ms`);
}
throw err;
} finally {
clearTimeout(timeoutId);
}
}
// Использование
try {
const data = await fetchWithTimeout('https://api.example.com/users', 3000);
console.log(data);
} catch (err) {
console.error(err.message); // Request timeout after 3000ms
}
Пример 2: Отмена при клике на кнопку отмены
class DataFetcher {
private controller: AbortController | null = null;
async startDownload(url: string) {
this.controller = new AbortController();
try {
const response = await fetch(url, {
signal: this.controller.signal
});
const data = await response.arrayBuffer();
console.log(`Downloaded ${data.byteLength} bytes`);
} catch (err) {
if (err.name === 'AbortError') {
console.log('Download cancelled by user');
} else {
console.error('Download error:', err);
}
}
}
cancelDownload() {
if (this.controller) {
this.controller.abort();
}
}
}
// Использование
const fetcher = new DataFetcher();
// Кнопка "Скачать"
document.getElementById('download-btn')?.addEventListener('click', () => {
fetcher.startDownload('https://example.com/largefile.zip');
});
// Кнопка "Отмена"
document.getElementById('cancel-btn')?.addEventListener('click', () => {
fetcher.cancelDownload();
});
Пример 3: Отмена параллельных операций
async function fetchMultipleUsers(userIds: string[]) {
const controller = new AbortController();
try {
const promises = userIds.map(id =>
fetch(`/api/users/${id}`, {
signal: controller.signal
}).then(r => r.json())
);
// Если одна операция падает, отменяем все остальные
const users = await Promise.all(promises);
return users;
} catch (err) {
controller.abort(); // Отменяем оставшиеся запросы
throw err;
}
}
Пример 4: AbortController с fs.promises в Node.js
import fs from 'fs/promises';
const controller = new AbortController();
// Отменить операцию чтения через 2 секунды
setTimeout(() => {
controller.abort();
}, 2000);
try {
// fs.readFile поддерживает signal в Node.js 15+
const data = await fs.readFile('large-file.txt', {
signal: controller.signal
});
console.log(data);
} catch (err) {
if (err.name === 'AbortError') {
console.log('File read cancelled');
}
}
Пример 5: AbortController с timeout helper
function withAbortTimeout<T>(
promise: Promise<T>,
timeoutMs: number
): Promise<T> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
return Promise.race([
promise,
new Promise<T>((_, reject) => {
controller.signal.addEventListener('abort', () => {
reject(new Error(`Operation timeout after ${timeoutMs}ms`));
});
})
]).finally(() => clearTimeout(timeoutId));
}
// Использование
const result = await withAbortTimeout(
fetch('/api/slow').then(r => r.json()),
5000
);
Когда использовать AbortController?
- HTTP запросы с таймаутом — fetch может зависнуть
- User-initiated cancellation — пользователь нажал "Отмена"
- Resource cleanup — остановить потоки, отменить операции
- Race conditions — отменить старые запросы при новых
- Server shutdown — корректно закрыть все in-flight операции
API, которые поддерживают AbortController
// Fetch API
fetch(url, { signal });
// fs.promises (Node.js 15+)
fs.readFile(path, { signal });
fs.writeFile(path, data, { signal });
// Timers (Node.js 15+)
setTimeout(() => {}, ms, { signal });
// EventTarget.addEventListener
eventTarget.addEventListener('event', handler, { signal });
// XMLHttpRequest (браузер)
xhr.abort(); // абстрактный механизм
// Custom операции
function customOperation(signal: AbortSignal) {
signal.addEventListener('abort', () => {
console.log('Operation aborted!');
});
}
Вывод
AbortController — это современный стандарт для управления жизненным циклом асинхронных операций. Без него приложение может утекать ресурсы, зависнуть на timeout'ы и плохо работать при отмене пользователем.