AbortController in JavaScript
AbortController is a special object that lets you create a "cancellation signal" (AbortSignal) and send a "cancel" command to asynchronous operations that listen to it. Put simply: AbortController manages the "cancelled or not" state, while AbortSignal is the flag that asynchronous code can watch.
Theory
TL;DR
new AbortController()creates a controller with two key members:signalandabort().controller.signalis anAbortSignalobject that you pass into an asynchronous operation.controller.abort()flips the signal into the cancelled state and fires all of its listeners.- In
fetch, an aborted request rejects with an error whoseerr.name === 'AbortError'. - The signal can also be supported in your own asynchronous functions: read
signal.abortedand listen for theabortevent. - A controller is single use: the next operation needs a new instance.
Quick example
javascript
const controller = new AbortController(); // create the controller
const signal = controller.signal; // take the signal out of it
fetch('https://example.com/data', { signal })
.then(response => response.json())
.then(data => console.log(data))
.catch(err => {
if (err.name === 'AbortError') {
console.log('The request was aborted');
} else {
console.error('Error:', err);
}
});
// Abort the request after 1 second:
setTimeout(() => {
controller.abort(); // cancellation
}, 1000);What happens:
- We create the controller, then pass
controller.signalintofetch. - When we call
controller.abort(),fetchis interrupted instantly and throws anAbortError.
How it works under the hood
AbortController creates an object with two key properties:
signal, anAbortSignalobject that can be passed into an asynchronous operation;abort(), a method that activates that signal: it changes the signal's state and calls every registered handler.
The controller itself cannot cancel anything. It only flips a flag, and the actual interruption is done by whichever API you handed the signal to.
What AbortSignal can do
AbortSignal:
- has an
.abortedproperty (a boolean:trueif the operation was cancelled); - can listen for the
"abort"event.
javascript
const controller = new AbortController();
const signal = controller.signal;
signal.addEventListener('abort', () => {
console.log('The operation was cancelled!');
});
controller.abort(); // => "The operation was cancelled!"Where you can use it
AbortController is supported by many APIs:
| API | Supports cancellation? |
|---|---|
fetch() | Yes |
ReadableStream, WritableStream | Yes |
WebSocket (through your own wrapper) | Partially |
setTimeout / setInterval (through the AbortSignal API in Node 20+ or a browser polyfill) | Yes |
navigator.sendBeacon() (in some browsers) | Partially |
| Your own async functions | Yes, if you implement the support by hand |
Example with a Promise and custom cancellation
You can use the signal inside your own asynchronous functions:
javascript
function wait(ms, signal) {
return new Promise((resolve, reject) => {
const timeout = setTimeout(resolve, ms);
signal.addEventListener('abort', () => {
clearTimeout(timeout);
reject(new Error('Cancelled by the user'));
});
});
}
const controller = new AbortController();
wait(5000, controller.signal)
.then(() => console.log('5 seconds have passed'))
.catch(err => console.log(err.message));
setTimeout(() => controller.abort(), 2000); // cancel after 2 secondsResult:
text
Cancelled by the userReuse and the API summary
A controller is single use:
- after
controller.abort()itssignalgets.aborted = trueforever; - to cancel again you have to create a new
AbortController.
| Property or method | What it does |
|---|---|
new AbortController() | creates a controller |
.signal | the signal to pass into an async API |
.abort() | sends the "cancel" command |
signal.aborted | true if cancelled |
signal.addEventListener('abort', fn) | listens for the cancellation event |
Common mistakes
- Reusing one controller for several requests. After the first
abort()the signal is already cancelled, so the nextfetchusing it fails immediately. Create a new controller per request. - Swallowing
AbortErrorlike an ordinary failure. Incatchyou must checkerr.name === 'AbortError'separately, otherwise the user sees a failure message where the request was cancelled on purpose. - Expecting
abort()to cancel the work on the server. Only the client side is cancelled: the browser tears down the connection, but the server may have already performed the operation. - Forgetting to remove the
abortlistener. In your own functions the listener should be removed once the work finishes (for example with{ once: true }), otherwise a long lived signal keeps a reference alive and leaks memory. - Ignoring an already aborted signal. If you write your own async function, check
signal.abortedat the start, because the signal may have been aborted before the function was even called.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.