AbortSignal - это объект-сигнал, через который одна часть кода сообщает другой: “операцию нужно отменить”.
Чаще всего AbortSignal получают из AbortController и передают в fetch().
const controller = new AbortController();
const signal = controller.signal;
fetch("https://learn-javascript.ru/api/users", {
signal,
});
controller.abort();
AbortController управляет отменой, а AbortSignal передаётся туда, где эту отмену нужно учитывать.
Как получить AbortSignal
Обычно AbortSignal не создают напрямую. Его берут из свойства signal у AbortController.
const controller = new AbortController();
console.log(controller.signal); // AbortSignal
В простом виде связка выглядит так:
const controller = new AbortController();
const signal = controller.signal;
controller.abort();
console.log(signal.aborted); // true
Метод abort() вызывают у controller, а не у signal.
Передача signal в fetch()
Чтобы запрос можно было отменить, signal передают в настройки fetch().
const controller = new AbortController();
try {
const response = await fetch("https://learn-javascript.ru/api/lessons", {
signal: controller.signal,
});
const lessons = await response.json();
console.log(lessons);
} catch (error) {
if (error.name === "AbortError") {
console.log("Запрос отменён");
}
}
Если позже вызвать controller.abort(), fetch() получит сигнал отмены и отклонит промис.
controller.abort();
Свойство aborted
Свойство aborted показывает, был ли сигнал уже отменён.
const controller = new AbortController();
const signal = controller.signal;
console.log(signal.aborted); // false
controller.abort();
console.log(signal.aborted); // true
Это удобно, если ваша функция должна заранее проверить, не отменили ли работу.
function doSomething(signal) {
if (signal.aborted) {
return;
}
console.log("Выполняем работу");
}
aborted только читают. Изменить его вручную нельзя.
Свойство reason
reason хранит причину отмены.
const controller = new AbortController();
controller.abort("Пользователь отменил загрузку");
console.log(controller.signal.reason);
// "Пользователь отменил загрузку"
Причиной может быть строка, объект или другое значение.
const controller = new AbortController();
controller.abort({
type: "navigation",
message: "Пользователь ушёл со страницы",
});
console.log(controller.signal.reason.type); // "navigation"
Если причина не нужна, можно вызывать abort() без аргументов.
Событие abort
AbortSignal умеет отправлять событие abort.
const controller = new AbortController();
const signal = controller.signal;
signal.addEventListener("abort", () => {
console.log("Операция отменена");
});
controller.abort();
Так можно подключить отмену к своему коду, а не только к fetch().
Например:
function startTimer(signal) {
const timerId = setInterval(() => {
console.log("Работаем...");
}, 1000);
signal.addEventListener("abort", () => {
clearInterval(timerId);
console.log("Таймер остановлен");
});
}
const controller = new AbortController();
startTimer(controller.signal);
controller.abort();
Когда сигнал отменяется, обработчик очищает таймер.
throwIfAborted()
Метод throwIfAborted() выбрасывает причину отмены, если сигнал уже отменён.
const controller = new AbortController();
controller.abort("Операция отменена");
controller.signal.throwIfAborted(); // выбросит "Операция отменена"
Этот метод удобен внутри своих асинхронных функций.
async function loadData(signal) {
signal.throwIfAborted();
const response = await fetch("https://learn-javascript.ru/api/data", {
signal,
});
signal.throwIfAborted();
return await response.json();
}
Так функция может быстро остановиться, если отмена уже произошла.
AbortSignal.timeout()
В современных браузерах можно создать сигнал, который отменится сам через заданное время.
const signal = AbortSignal.timeout(3000);
try {
const response = await fetch("https://learn-javascript.ru/api/articles", {
signal,
});
const articles = await response.json();
console.log(articles);
} catch (error) {
console.log("Запрос отменён или завершился ошибкой");
}
Так можно сделать таймаут без ручного setTimeout() и clearTimeout().
Если нужно поддерживать старое окружение, таймаут можно сделать через обычный AbortController.
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
}, 3000);
try {
await fetch("https://learn-javascript.ru/api/articles", {
signal: controller.signal,
});
} finally {
clearTimeout(timeoutId);
}
AbortSignal.any()
AbortSignal.any() создаёт новый сигнал, который отменится, когда отменится любой сигнал из списка.
const controller = new AbortController();
const timeoutSignal = AbortSignal.timeout(5000);
const signal = AbortSignal.any([controller.signal, timeoutSignal]);
fetch("https://learn-javascript.ru/api/search", {
signal,
});
Теперь запрос можно отменить вручную:
controller.abort();
или он отменится сам по таймауту.
Это полезно, когда у операции есть несколько причин для отмены: пользователь нажал кнопку, страница закрылась, истекло время ожидания.
Один signal нельзя сбросить обратно
Если сигнал отменён, он остаётся отменённым.
const controller = new AbortController();
const signal = controller.signal;
controller.abort();
console.log(signal.aborted); // true
Такой сигнал нельзя “включить заново”.
Для новой операции создавайте новый AbortController и новый signal.
const nextController = new AbortController();
fetch("https://learn-javascript.ru/api/users", {
signal: nextController.signal,
});
Частые ошибки
Вызывать abort() у signal
const controller = new AbortController();
controller.signal.abort(); // TypeError
Отмену запускает controller.
controller.abort();
Передать уже отменённый signal в новый запрос
const controller = new AbortController();
controller.abort();
fetch("https://learn-javascript.ru/api/users", {
signal: controller.signal,
}); // запрос сразу отменится
Для нового запроса нужен новый controller.
Проверять только aborted и забывать про catch
const controller = new AbortController();
if (!controller.signal.aborted) {
fetch("https://learn-javascript.ru/api/users", {
signal: controller.signal,
});
}
Сигнал может отмениться уже после старта запроса. Поэтому для fetch() всё равно нужна обработка catch.
Быстрая памятка
AbortSignalобычно получают черезcontroller.signalsignalпередают вfetch()и другие операции, которые умеют отменяться- отмену запускает
controller.abort() signal.abortedпоказывает, был ли сигнал отменёнsignal.reasonхранит причину отмены- событие
abortможно слушать черезaddEventListener() throwIfAborted()выбрасывает причину, если сигнал уже отменёнAbortSignal.timeout(ms)создаёт сигнал с автоматической отменойAbortSignal.any([...])объединяет несколько сигналов- отменённый signal нельзя сбросить обратно
Коротко
AbortSignal - это объект, который передаёт сигнал отмены в fetch() и другой асинхронный код. Его обычно берут из AbortController, передают в операцию, а затем отменяют через controller.abort(). По самому signal можно проверить aborted, прочитать reason, подписаться на событие abort и построить более аккуратную логику отмены.