Привет, Хабр!
Сегодня мы рассмотрим интересный инструмент в JS. AbortController в JS — инструмент, который позволяет отменять асинхронные операции в любой момент. Разберёмся, как он работает, где пригодится и какие у него есть проблемы.
AbortController — это инструмент для принудительной остановки асинхронных операций в JavaScript.
Например, можно:
1. Остановить fetch()‑запрос, если он уже не нужен.
2. Прервать таймер (setTimeout(), setInterval()).
3. Отменить стриминг данных (ReadableStream).
Как работает AbortController:
Создаём новый контроллер:
const controller = new AbortController();Получаем сигнал (signal):
const signal = controller.signal;Передаём signal в асинхронную операцию (
fetch,setTimeoutи т. д.)Вызываем
.abort(), когда нужно прервать операцию.
Основные методы AbortController
AbortController.signal
Возвращает объект AbortSignal, который сообщает, когда асинхронная операция должна быть прервана.
const controller = new AbortController(); console.log(controller.signal); // AbortSignal { aborted: false }
AbortController.abort()
Прерывает все операции, использующие signal. После этого signal.aborted === true.
const controller = new AbortController(); controller.abort(); console.log(controller.signal.aborted); // true
signal.addEventListener(“abort”, callback)
Можно подписаться на событие abort и выполнить действия при отмене.
const controller = new AbortController(); controller.signal.addEventListener("abort", () => console.log("Операция отменена")); setTimeout(() => controller.abort(), 2000);
Как отменять fetch()-запросы
Один из самых популярных случаев применения AbortController — управление сетевыми запросами.
Пример без AbortController (плохо):
async function fetchData() { const response = await fetch("https://api.example.com/data"); const data = await response.json(); console.log(data); } fetchData(); setTimeout(() => console.log("Что делать, если запрос уже не нужен?"), 1000);
Этот запрос нельзя отменить. Даже если он больше не нужен, браузер его дождётся до конца.
Используем AbortController (правильно):
const controller = new AbortController(); const signal = controller.signal; async function fetchData() { try { const response = await fetch("https://api.example.com/data", { signal }); const data = await response.json(); console.log(data); } catch (err) { if (err.name === "AbortError") { console.log("Запрос был отменён!"); } else { console.error("Ошибка:", err); } } } // Запускаем запрос fetchData(); // Отменяем его через 1 секунду setTimeout(() => controller.abort(), 1000);
Теперь запрос не будет тратить ресурсы браузера, если он больше не нужен.
Три кейса применения AbortController
Кейс 1: отмена запросов в поисковой строке
При каждом вводе символа браузер делает новый запрос, но старые продолжают выполняться.
let controller; async function search(query) { if (controller) controller.abort(); // Отменяем предыдущий запрос controller = new AbortController(); const signal = controller.signal; try { const response = await fetch(`https://api.example.com/search?q=${query}`, { signal }); const data = await response.json(); console.log("Результаты:", data); } catch (err) { if (err.name === "AbortError") { console.log("Предыдущий запрос отменён"); } else { console.error("Ошибка:", err); } } } document.querySelector("#search").addEventListener("input", (e) => { search(e.target.value); });
Теперь браузер не тратит ресурсы на старые ненужные запросы.
Кейс 2: остановка таймеров и фоновых задач
AbortController позволяет управлять setTimeout() и останавливать фоновые операции.
const controller = new AbortController(); const signal = controller.signal; function delayedTask() { if (signal.aborted) { console.log("Таймер отменён"); return; } setTimeout(() => { if (!signal.aborted) { console.log("Задача выполнена"); } }, 5000); } // Останавливаем выполнение через 2 секунды setTimeout(() => controller.abort(), 2000); delayedTask();
Таймер не выполнится, если .abort() вызван раньше.
Кейс 3: отмена загрузки файлов (стриминг данных)
AbortController работает с ReadableStream, позволяя прерывать загрузку файлов.
const controller = new AbortController(); const signal = controller.signal; async function fetchLargeFile() { try { const response = await fetch("https://example.com/largefile.zip", { signal }); const reader = response.body.getReader(); let receivedLength = 0; let chunks = []; while (true) { const { done, value } = await reader.read(); if (done) break; chunks.push(value); receivedLength += value.length; console.log(`Загружено ${receivedLength} байт`); } } catch (err) { if (err.name === "AbortError") { console.log("Загрузка отменена"); } else { console.error("Ошибка:", err); } } } document.querySelector("#download").addEventListener("click", fetchLargeFile); document.querySelector("#abort").addEventListener("click", () => controller.abort());
Если нажать «Отмена», загрузка файла прервётся, а браузер освободит ресурсы.
Возможные проблем
AbortController нельзя переиспользовать после
.abort()const controller = new AbortController(); controller.abort(); controller.abort(); // Ошибка, сигнал уже отменёнПоэтому создавайте новый AbortController для каждой операции.
Не все API поддерживают AbortSignal
Например, XMLHttpRequest не работает с AbortController.Обработку AbortError надо учитывать
fetch(url, { signal }) .catch(err => { if (err.name === "AbortError") { console.log("Запрос отменён"); } else { console.error("Ошибка:", err); } });
А какие задачи с AbortController решали вы? Делитесь в комментариях!
19 марта пройдет открытый урок на тему «Прототипное наследование в JavaScript». Записаться бесплатно можно на странице курса "Fullstack developer".
Все темы открытых уроков можно посмотреть в календаре.
