JavaScript AbortController: Cancel Async Requests
The AbortController interface in JavaScript provides a
standardized way to cancel asynchronous operations, such as Fetch API
requests, streams, or custom asynchronous tasks, before they complete.
By pairing an AbortController instance with an
AbortSignal, developers can communicate cancellation events
across different parts of an application, preventing memory leaks,
reducing unnecessary network bandwidth, and avoiding race conditions
when handling outdated data.
How AbortController Works
The cancellation mechanism relies on two primary components:
AbortController: The controller object that initiates the abort action via its.abort()method.AbortSignal: A read-only property (controller.signal) passed to the asynchronous operation, acting as an event listener that notifies the operation when an abort has been requested.
When controller.abort() is invoked, the associated
AbortSignal changes its aborted state to
true and dispatches an abort event. Any
consumer observing that signal immediately terminates its operation.
Canceling a Fetch Request
The most common use case for AbortController is
canceling HTTP requests initiated by the Fetch API.
const controller = new AbortController();
const signal = controller.signal;
// Pass the signal inside the fetch options object
fetch('https://api.example.com/data', { signal })
.then(response => response.json())
.then(data => console.log('Data received:', data))
.catch(error => {
if (error.name === 'AbortError') {
console.log('Fetch request was successfully canceled.');
} else {
console.error('An unexpected error occurred:', error);
}
});
// Cancel the request
controller.abort();When controller.abort() runs, the fetch()
promise rejects with a DOMException named
AbortError. Wrapping the request in a standard
try...catch or .catch() block allows you to
intercept this error and handle the cancellation gracefully without
breaking application flow.
Canceling Multiple Requests Simultaneously
A single AbortSignal can be assigned to multiple
asynchronous operations. Calling .abort() on the parent
controller cancels all linked operations at once:
const controller = new AbortController();
const { signal } = controller;
const fetchUsers = fetch('/api/users', { signal });
const fetchPosts = fetch('/api/posts', { signal });
// Cancels both requests simultaneously
controller.abort();Canceling Custom Asynchronous Operations
AbortSignal is an EventTarget, meaning it
can be used to cancel custom operations like timers or background
calculations using the abort event listener:
function delay(ms, signal) {
return new Promise((resolve, reject) => {
if (signal?.aborted) {
return reject(new DOMException('Aborted', 'AbortError'));
}
const timer = setTimeout(() => {
resolve('Operation completed');
}, ms);
signal?.addEventListener('abort', () => {
clearTimeout(timer);
reject(new DOMException('Aborted', 'AbortError'));
});
});
}
const controller = new AbortController();
delay(5000, controller.signal).catch(err => console.log(err.message));
// Cancel the timer after 1 second
setTimeout(() => controller.abort(), 1000);Automatic Timeouts
with AbortSignal.timeout()
Modern JavaScript environments support
AbortSignal.timeout(), which automatically creates a signal
that triggers an abort after a specified duration:
// Automatically aborts if the request takes longer than 3 seconds
fetch('https://api.example.com/data', { signal: AbortSignal.timeout(3000) })
.then(response => response.json())
.catch(error => {
if (error.name === 'TimeoutError') {
console.log('Request timed out.');
}
});Key Practical Applications
- Component Unmounting: Canceling in-flight requests in frameworks like React or Vue when a user navigates away from a page.
- Auto-complete / Debouncing: Aborting previous search queries when a user types a new character to avoid race conditions.
- Request Timeouts: Enforcing maximum response times on network operations without external libraries.