Fetch: abort
Learn how to cancel fetch requests in JavaScript with AbortController and AbortSignal. Covers user-triggered aborts and request timeouts.
Once a fetch() request is in flight, it keeps running until the server responds — even if the user has navigated away, typed a new search term, or the answer is no longer needed. Those wasted requests tie up connections, burn battery and bandwidth, and can deliver stale results that overwrite fresh ones. The AbortController interface gives you a clean, standard way to cancel a request on demand.
This chapter shows how to wire up AbortController, react to user actions, build timeouts, cancel several requests at once, and handle the resulting AbortError properly. To find out more about fetch itself, see the previous page, Fetch API.
How AbortController works
AbortController is a tiny object with one job: it owns an AbortSignal and can flip that signal to the aborted state. The signal is the part you hand to fetch() (and to many other browser APIs, such as addEventListener). When you call controller.abort(), every operation that received that signal is cancelled.
The pattern is always the same three steps:
- Create a controller:
const controller = new AbortController(). - Pass
controller.signaltofetch()in the options object. - Call
controller.abort()whenever you want to cancel.
When a fetch is aborted, its promise rejects with a DOMException whose name is "AbortError". That is why every example below checks error.name === 'AbortError' — so you can ignore the deliberate cancellation while still surfacing real network failures.
Basic usage of AbortController
Here is the smallest complete example. It aborts immediately so you can see the rejection path:
We create an AbortController, pass its signal to fetch(), and immediately call controller.abort(). Because the request never completes normally, the .catch() runs and reports the abort.
Inspecting the signal: aborted and the abort event
The signal exposes its state so other code can react to a cancellation. Two members matter most:
signal.aborted— a boolean that becomestrueonce aborted.- the
"abort"event — fired on the signal the momentabort()is called.
This is handy when you have non-fetch work (a timer, an animation, a stream reader) that should stop the moment the request is cancelled.
One controller, one use. A controller cannot be reset. Once you call
abort(), that signal stays aborted forever, and any newfetch()you start with it rejects instantly. For a fresh request, create a newAbortController.
Practical example: aborting on a user action
The most common reason to abort is a user changing their mind — clicking "Cancel", closing a dialog, or typing a new query before the previous one finished. Here a button cancels an in-progress request:
<body>
<button id="abortButton">Abort Fetch Request</button>
<script>
const controller = new AbortController();
const signal = controller.signal;
document.getElementById('abortButton').addEventListener('click', () => {
controller.abort();
});
fetch('https://httpbin.org/delay/5', { signal })
.then(response => response.json())
.then(data => alert(
'Data is successfully fetched! Refresh the page and try aborting.'
))
.catch(error => {
if (error.name === 'AbortError') {
alert('Fetch request was aborted by the user');
} else {
alert('Fetch error: ' + error.message);
}
});
</script>
</body>Clicking the button with the ID abortButton cancels the ongoing fetch request. The endpoint https://httpbin.org/delay/5 deliberately takes 5 seconds, so if you click within that window the request rejects with AbortError.
Aborting several requests at once
A single signal can be passed to many requests. One call to controller.abort() then cancels all of them — useful when a page leaves a view that started several parallel loads:
Because all three requests share one signal, controller.abort() cancels them in a single call and Promise.all rejects with AbortError. To learn more about running requests in parallel, see Promise API.
Aborting after a timeout
A very common use of AbortController is to give a request a deadline: if the server is too slow, cancel and show an error instead of waiting forever.
The manual way with setTimeout
You can pair the controller with a timer and combine it with any other async logic. Here a separate operation triggers the abort after one second, while the endpoint would take five:
The fetch is aborted by the timer after one second, well before the five-second endpoint can respond. Read more about timers in async/await.
The shortcut: AbortSignal.timeout()
Modern browsers (and Node 17.3+) ship a built-in helper that creates a signal which aborts itself after a given number of milliseconds — no controller or setTimeout needed:
Note that a timeout abort rejects with a TimeoutError, not an AbortError, so you can tell "took too long" apart from "the user cancelled". If you need both a timeout and a manual cancel button, combine signals with AbortSignal.any([userSignal, AbortSignal.timeout(2000)]).
Handling AbortError correctly
Whenever you abort a fetch, the promise rejects. Forgetting to handle it produces a noisy "uncaught promise rejection" in the console, even though the cancellation was intentional. Two rules keep things clean:
- Always have a
.catch()(ortry/catchwithawait) on an abortable fetch. - Inside it, check
error.nameand treat'AbortError'/'TimeoutError'as expected — only log or surface the other errors. See Error handling with promises for the broader pattern.
Conclusion
AbortController is the standard way to cancel fetch() requests in JavaScript. Create a controller, pass its signal to one or more requests, and call abort() whenever the work is no longer needed. You have seen user-triggered cancellation, manual and built-in timeouts, cancelling many requests at once, and how to handle the resulting AbortError. Reaching for these patterns keeps your apps responsive and stops them from wasting time on results nobody is waiting for.