AbortSignal
Базовая реализация Широко доступна *
Эта функция хорошо зарекомендовала себя и работает на многих устройствах и версиях браузеров. Она доступна в браузерах с марта 2019 года.
* Некоторые части этой функции могут иметь различный уровень поддержки.
Примечание: Эта функция доступна в Web Workers.
Интерфейс AbortSignal представляет собой объект сигнала, который позволяет взаимодействовать с асинхронной операцией (такой как запрос fetch) и прервать ее при необходимости через объект AbortController.
Свойства экземпляра
Также наследует свойства от своего родительского интерфейса, EventTarget.
-
AbortSignal.abortedТолько для чтения -
Логическое значение, указывающее, прервана ли операция (
true) или нет (false). -
AbortSignal.reasonТолько для чтения -
Значение JavaScript, содержащее причину прерывания, после того как сигнал был прерван.
Статические методы
Также наследует методы от своего родительского интерфейса, EventTarget.
AbortSignal.abort()-
Возвращает экземпляр
AbortSignal, который уже установлен как прерванный. AbortSignal.any()-
Возвращает
AbortSignal, который прерывает работу, когда любой из заданных сигналов прерывания прерывается. AbortSignal.timeout()-
Возвращает экземпляр
AbortSignal, который автоматически прерывает работу после указанного времени.
Методы экземпляра
Также наследует методы от своего родительского интерфейса, EventTarget.
AbortSignal.throwIfAborted()-
Выбрасывает причину прерывания сигнала (
reason), если сигнал прерван; в противном случае ничего не делает.
События
Также наследует события от своего родительского интерфейса, EventTarget.
Подпишитесь на это событие, используя addEventListener() или назначив обработчик событий свойству oneventname этого интерфейса.
abort-
Вызывается, когда асинхронные операции, с которыми взаимодействует сигнал, прерываются. Также доступно через свойство
onabort.
Примеры
Прерывание операции fetch с использованием явного сигнала
Следующий фрагмент кода демонстрирует, как можно использовать сигнал для прерывания загрузки видео с помощью API Fetch.
Сначала создается контроллер прерывания с использованием конструктора AbortController(), затем берется ссылка на связанный объект AbortSignal с помощью свойства AbortController.signal.
При инициализации запроса fetch мы передаем AbortSignal в качестве параметра в объекте опций запроса ({signal} ниже). Это связывает сигнал и контроллер с запросом fetch и позволяет прервать его, вызвав AbortController.abort(). Ниже показано, что операция fetch прерывается во втором обработчике событий, который был вызван при нажатии кнопки "Прервать" (abortBtn).
Когда вызывается abort(), обещание fetch() отклоняется с исключением типа DOMException с именем AbortError.
let controller;
const url = "video.mp4";
const downloadBtn = document.querySelector(".download");
const abortBtn = document.querySelector(".abort");
downloadBtn.addEventListener("click", fetchVideo);
abortBtn.addEventListener("click", () => {
if (controller) {
controller.abort();
console.log("Download aborted");
}
});
async function fetchVideo() {
controller = new AbortController();
const signal = controller.signal;
try {
const response = await fetch(url, { signal });
console.log("Download complete", response);
// process response further
} catch (err) {
console.error(`Download error: ${err.message}`);
}
}
Если запрос прерывается после выполнения вызова fetch() но до чтения тела ответа, то попытка чтения тела ответа приведет к отклонению с исключением типа AbortError.
async function get() {
const controller = new AbortController();
const request = new Request("https://example.org/get", {
signal: controller.signal,
});
const response = await fetch(request);
controller.abort();
// The next line will throw `AbortError`
const text = await response.text();
console.log(text);
}
Полный рабочий пример можно найти на GitHub; также его можно посмотреть в живом режиме.
Прерывание операции fetch по таймауту
Если вам нужно прервать операцию по таймауту, можно использовать статический метод AbortSignal.timeout(). Он возвращает AbortSignal объект, который автоматически прерывается по истечении определенного количества миллисекунд.
Фрагмент кода ниже демонстрирует, как можно либо успешно загрузить файл, либо обработать ошибку таймаута через 5 секунд. Обратите внимание, что при таймауте обещание fetch() отклоняется с исключением TimeoutError DOMException. Это позволяет коду различать таймауты (для которых, вероятно, потребуется уведомление пользователя) и явные прерывания пользователем.
const url = "video.mp4";
try {
const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
const result = await res.blob();
// …
} catch (err) {
if (err.name === "TimeoutError") {
console.error("Timeout: It took more than 5 seconds to get the result!");
} else if (err.name === "AbortError") {
console.error(
"Fetch aborted by user action (browser stop button, closing tab, etc.",
);
} else {
// A network error, or some other problem.
console.error(`Error: type: ${err.name}, message: ${err.message}`);
}
}
Прерывание fetch с таймаутом или явным прерыванием
Если вы хотите прервать работу по нескольким сигналам, можно использовать AbortSignal.any() для их объединения в один сигнал. Следующий пример демонстрирует это использование fetch:
try {
const controller = new AbortController();
const timeoutSignal = AbortSignal.timeout(5000);
const res = await fetch(url, {
// This will abort the fetch when either signal is aborted
signal: AbortSignal.any([controller.signal, timeoutSignal]),
});
const body = await res.json();
} catch (e) {
if (e.name === "AbortError") {
// Notify the user of abort.
} else if (e.name === "TimeoutError") {
// Notify the user of timeout
} else {
// A network error, or some other problem.
console.log(`Type: ${e.name}, Message: ${e.message}`);
}
}
Примечание: В отличие от использования AbortSignal.timeout(), нет способа определить, вызвано ли окончательное прерывание таймаутом.
Реализация API с возможностью прерывания
API, которому требуется поддержка прерывания, может принимать объект AbortSignal и использовать его состояние для запуска обработки сигнала прерывания при необходимости.
API, основанный на Promise, должен реагировать на сигнал прерывания, отклоняя любое невыполненное обещание с исключением AbortSignal прерывания (reason ). Например, рассмотрите следующий myCoolPromiseAPI, который принимает сигнал и возвращает обещание. Обещание отклоняется немедленно, если сигнал уже прерван или если обнаружено событие прерывания. В противном случае оно завершается нормально, а затем выполняет обещание.
function myCoolPromiseAPI(/* …, */ { signal }) {
return new Promise((resolve, reject) => {
// If the signal is already aborted, immediately throw in order to reject the promise.
if (signal.aborted) {
reject(signal.reason);
}
// Perform the main purpose of the API
// Call resolve(result) when done.
// Watch for 'abort' signals
signal.addEventListener("abort", () => {
// Stop the main operation
// Reject the promise with the abort reason.
reject(signal.reason);
});
});
}
API может быть затем использовано, как показано. Обратите внимание, что AbortController.abort() вызывается для прерывания операции.
const controller = new AbortController();
const signal = controller.signal;
startSpinner();
myCoolPromiseAPI({ /* …, */ signal })
.then((result) => {})
.catch((err) => {
if (err.name === "AbortError") return;
showUserErrorMessage();
})
.then(() => stopSpinner());
controller.abort();
API, которые не возвращают обещания, могут реагировать аналогичным образом. В некоторых случаях может иметь смысл игнорировать сигнал.
Спецификации
| Спецификация |
|---|
| DOM # interface-AbortSignal |
Совместимость браузеров
| Рабочий стол | Мобильный | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Opera | Safari | Chrome Android | Firefox для Android | Opera Android | Safari на IOS | Samsung Internet | WebView Android | |
AbortSignal |
66 | 16 | 57 | 53 | 11.1 | 66 | 57 | 47 | 11.3 | 9.0 | 66 |
abort_event |
66 | 16 | 57 | 53 | 11.1 | 66 | 57 | 47 | 11.3 | 9.0 | 66 |
abort_static |
93 | 93 | 88 | 79 | 15 | 93 | 88 | 66 | 15 | 17.0 | 93 |
aborted |
66 | 16 | 57 | 53 | 11.1 | 66 | 57 | 47 | 11.3 | 9.0 | 66 |
any_static |
116 | 116 | 124 | 102 | 17.4 | 116 | 124 | 78 | 17.4 | 24.0 | 116 |
reason |
98 | 98 | 97 | 84 | 15.4 | 98 | 97 | 68 | 15.4 | 18.0 | 98 |
throwIfAborted |
100 | 100 | 97 | 86 | 15.4 | 100 | 97 | 69 | 15.4 | 19.0 | 100 |
timeout_static |
124103–124Всегда прерывается сAbortError по истечении времени ожидания, а не с TimeoutError. |
124103–124Всегда прерывается сAbortError по истечении времени ожидания, а не с TimeoutError. |
100 | 11089–110Всегда прерывается сAbortError по истечении времени ожидания, а не с TimeoutError. |
16 | 124103–124Всегда прерывается сAbortError по истечении времени ожидания, а не с TimeoutError. |
100 | 8271–82Всегда прерывается сAbortError по истечении времени ожидания, а не с TimeoutError. |
16 | 27.020.0–27.0Всегда прерывается сAbortError по истечении времени ожидания, а не с TimeoutError. |
124103–124Всегда прерывается сAbortError по истечении времени ожидания, а не с TimeoutError. |
См. также
- API Fetch
- Прерываемый Fetch Джека Арчибальда
© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal