AbortSignal
Baseline
Widely available
*
This feature is well established and works across many devices and browser versions. It’s been available across browsers since April 2018.
* Some parts of this feature may have varying levels of support.
Note: This feature is available in Web Workers.
The AbortSignal interface represents a signal object that allows you to communicate with an asynchronous operation (such as a fetch request) and abort it if required via an AbortController object.
Instance properties
Also inherits properties from its parent interface, EventTarget.
AbortSignal.abortedRead only-
A Boolean that indicates whether the request(s) the signal is communicating with is/are aborted (
true) or not (false). AbortSignal.reasonRead only-
A JavaScript value providing the abort reason, once the signal has aborted.
Static methods
Also inherits methods from its parent interface, EventTarget.
AbortSignal.abort()-
Returns an
AbortSignalinstance that is already set as aborted. AbortSignal.any()-
Returns an
AbortSignalthat aborts when any of the given abort signals abort. AbortSignal.timeout()-
Returns an
AbortSignalinstance that will automatically abort after a specified time.
Instance methods
Also inherits methods from its parent interface, EventTarget.
AbortSignal.throwIfAborted()-
Throws the signal's abort
reasonif the signal has been aborted; otherwise it does nothing.
Events
Also inherits events from its parent interface, EventTarget.
Listen to this event using addEventListener() or by assigning an event listener to the oneventname property of this interface.
abort-
Invoked when the asynchronous operations the signal is communicating with is/are aborted. Also available via the
onabortproperty.
Examples
Aborting a fetch operation using an explicit signal
The following snippet shows how we might use a signal to abort downloading a video using the Fetch API.
We first define a variable for our AbortController.
Before each fetch request we create a new controller using the AbortController() constructor, then grab a reference to its associated AbortSignal object using the AbortController.signal property.
Note:
An AbortSignal can only be used once. After it is aborted, any fetch call using the same signal will be immediately rejected.
When the fetch request is initiated, we pass in the AbortSignal as an option inside the request's options object (the { signal } below). This associates the signal and controller with the fetch request and allows us to abort it by calling AbortController.abort(), as seen below in the second event listener.
When abort() is called, the fetch() promise rejects with a DOMException named 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}`);
}
}
If the request is aborted after the fetch() call has been fulfilled but before the response body has been read, then attempting to read the response body will reject with an AbortError exception.
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);
}
You can find a full working example on GitHub; you can also see it running live.
Aborting a fetch operation with a timeout
If you need to abort the operation on timeout then you can use the static AbortSignal.timeout() method.
This returns an AbortSignal that will automatically timeout after a certain number of milliseconds.
The code snippet below shows how you would either succeed in downloading a file, or handle a timeout error after 5 seconds.
Note that when there is a timeout the fetch() promise rejects with a TimeoutError DOMException.
This allows code to differentiate between timeouts (for which user notification is probably required), and user aborts.
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}`);
}
}
Aborting a fetch with timeout or explicit abort
If you want to abort from multiple signals, you can use AbortSignal.any() to combine them into a single signal. The following example shows this using 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}`);
}
}
Note:
Unlike when using AbortSignal.timeout(), there is no way to tell whether the final abort was caused by a timeout.
Removing the abort event listener
Signals created by AbortControllers are garbage collectible as soon as both the signal and its owning controller become unreachable, even with abort event listeners, because it's guaranteed that the event won't fire. However, signals whose abortion is managed by something other than a controller are kept alive by the existence of an abort event listener:
- A non-aborted signal returned by
AbortSignal.any()is kept alive while it still has source signals and either attachedabortlisteners or internal abort steps registered by an API. - A signal returned by
AbortSignal.timeout()is kept alive while its timeout is pending and it has attachedabortlisteners.
The following function combines application-wide cancellation with a signal supplied by the caller for an individual operation. It adds a listener to log cancellation, but relies on { once: true } to remove it:
const globalController = new AbortController();
async function doOperation(url, localSignal) {
const signal = AbortSignal.any([globalController.signal, localSignal]);
signal.addEventListener("abort", () => console.log(`Aborted: ${url}`), {
once: true,
});
const response = await fetch(url, { signal });
return response.text();
}
{ once: true } only removes the listener when the event fires. If neither input signal aborts, the listener remains even after the response body has been read. Repeated calls can therefore retain combined signals and their listeners for as long as the global signal remains reachable and the combined signals remain non-aborted. Discarding the combined signal does not remove the listener, and fetch() does not clean up listeners added by your code.
Instead, remove the listener when the operation finishes, whether it succeeds or fails. Use a named listener so you can remove it in a finally block:
async function doOperation(url, localSignal) {
const signal = AbortSignal.any([globalController.signal, localSignal]);
const onAbort = () => console.log(`Aborted: ${url}`);
signal.addEventListener("abort", onAbort, { once: true });
try {
const response = await fetch(url, { signal });
return await response.text();
} finally {
signal.removeEventListener("abort", onAbort);
}
}
The await on response.text() ensures the listener remains registered until the response body has been read. This cleanup is for the listener added by the example, not for fetch()'s internal abort handling. If an operation only needs application-wide cancellation, pass globalController.signal directly instead of creating a combined signal.
Implementing an abortable API
An API that needs to support aborting can accept an AbortSignal object, and use its state to trigger abort signal handling when needed.
A Promise-based API should respond to the abort signal by rejecting any unsettled promise with the AbortSignal abort reason.
For example, consider the following myCoolPromiseAPI, which takes a signal and returns a promise.
The promise is rejected immediately if the signal is already aborted, or if the abort event is detected.
Otherwise it completes normally after a delay and resolves the promise.
Remove the abort listener when the operation completes normally, so a long-lived signal does not retain the listener and the values it references. Again, { once: true } only removes the listener if the signal actually aborts.
function myCoolPromiseAPI(/* …, */ { signal }) {
return new Promise((resolve, reject) => {
// If the signal is already aborted, immediately throw in order to reject the promise.
signal.throwIfAborted();
// Simulate the main operation completing after a delay.
const timeoutId = setTimeout(() => {
signal.removeEventListener("abort", onAbort);
resolve("Operation completed");
}, 1000);
function onAbort() {
// Stop the main operation and reject with the abort reason.
clearTimeout(timeoutId);
reject(signal.reason);
}
signal.addEventListener("abort", onAbort, { once: true });
});
}
The API might then be used as shown.
Note that AbortController.abort() is called to abort the operation.
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();
APIs that do not return promises might react in a similar manner. In some cases it may make sense to absorb the signal.
Specifications
| Specification |
|---|
| DOM # interface-AbortSignal |
Browser compatibility
See also
- Fetch API
- Abortable Fetch by Jake Archibald