Fetch fires, the user navigates away, and the response arrives to a component that no longer exists. AbortController lets you cancel in-flight requests, timers, and streams so you stop wasting bandwidth and avoid updating unmounted UI.
What is AbortController?
AbortController provides a way to forcefully stop async operations in JavaScript. Some common use cases include:
- Canceling a fetch() request if it’s no longer needed.
- Stopping a timer (
setTimeout(),setInterval()). - Aborting data streaming (
ReadableStream).
How AbortController Works
- Create an AbortController instance:
-
js
const abortHandler = new AbortController();
-
- Retrieve the signal from the controller:
-
js
const abortSignal = abortHandler.signal;
-
- Pass the signal to an asynchronous operation (
fetch,setTimeout, etc.). - Call
.abort()to terminate the operation when needed.
Key Methods of AbortController
AbortController.signal
Returns an AbortSignal object that tracks when an async operation should be aborted.
const abortHandler = new AbortController();
console.log(abortHandler.signal); // AbortSignal { aborted: false }AbortController.abort()
Cancels all operations linked to the signal. After calling .abort(), signal.aborted becomes true.
const abortHandler = new AbortController();
abortHandler.abort();
console.log(abortHandler.signal.aborted); // truesignal.addEventListener("abort", callback)
Executes a function when an operation is aborted.
const abortHandler = new AbortController();
abortHandler.signal.addEventListener('abort', () =>
console.log('Operation aborted')
);
setTimeout(() => abortHandler.abort(), 2000);Canceling fetch() Requests
Without AbortController (Not Recommended)
async function loadData() {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
console.log(result);
}
loadData();
setTimeout(() => console.log('What if the request is no longer needed?'), 1000);This request cannot be stopped, consuming unnecessary resources.
With AbortController (Recommended)
const abortHandler = new AbortController();
const abortSignal = abortHandler.signal;
async function loadData() {
try {
const response = await fetch('https://api.example.com/data', {
signal: abortSignal,
});
const result = await response.json();
console.log(result);
} catch (err) {
if (err.name === 'AbortError') {
console.log('Request was canceled!');
} else {
console.error('Error:', err);
}
}
}
loadData();
setTimeout(() => abortHandler.abort(), 1000);This ensures that if a request is no longer needed, resources are freed up immediately.
Additional Example: Canceling Multiple Fetch Requests
const fetchControllers = new Map();
async function fetchWithCancel(url, requestId) {
if (fetchControllers.has(requestId)) {
fetchControllers.get(requestId).abort();
}
const newAbortHandler = new AbortController();
fetchControllers.set(requestId, newAbortHandler);
try {
const response = await fetch(url, { signal: newAbortHandler.signal });
const data = await response.json();
console.log('Fetched Data:', data);
} catch (err) {
if (err.name === 'AbortError') {
console.log(`Request ${requestId} was canceled`);
} else {
console.error('Error:', err);
}
}
}
fetchWithCancel('https://api.example.com/data', 'request1');
setTimeout(
() => fetchWithCancel('https://api.example.com/data', 'request1'),
500
);Use Cases for AbortController
1. Canceling Search Requests in Input Fields
When users type in a search box, multiple API calls can be triggered. We can cancel previous calls to save resources.
let searchAbortHandler;
async function search(query) {
if (searchAbortHandler) searchAbortHandler.abort(); // Cancel previous request
searchAbortHandler = new AbortController();
const abortSignal = searchAbortHandler.signal;
try {
const response = await fetch(`https://api.example.com/search?q=${query}`, {
signal: abortSignal,
});
const result = await response.json();
console.log('Results:', result);
} catch (err) {
if (err.name === 'AbortError') {
console.log('Previous request canceled');
} else {
console.error('Error:', err);
}
}
}
document.querySelector('#search').addEventListener('input', e => {
search(e.target.value);
});2. Stopping Timers and Background Tasks
AbortController can manage setTimeout() and cancel tasks before execution.
const timerAbortHandler = new AbortController();
const timerAbortSignal = timerAbortHandler.signal;
function executeDelayedTask() {
if (timerAbortSignal.aborted) {
console.log('Timer canceled');
return;
}
setTimeout(() => {
if (!timerAbortSignal.aborted) {
console.log('Task executed');
}
}, 5000);
}
setTimeout(() => timerAbortHandler.abort(), 2000);
executeDelayedTask();3. Canceling File Downloads (Streaming Data)
const downloadAbortHandler = new AbortController();
const downloadAbortSignal = downloadAbortHandler.signal;
async function downloadLargeFile() {
try {
const response = await fetch('https://example.com/largefile.zip', {
signal: downloadAbortSignal,
});
const reader = response.body.getReader();
let receivedBytes = 0;
let chunks = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
receivedBytes += value.length;
console.log(`Downloaded ${receivedBytes} bytes`);
}
} catch (err) {
if (err.name === 'AbortError') {
console.log('Download canceled');
} else {
console.error('Error:', err);
}
}
}
document
.querySelector('#startDownload')
.addEventListener('click', downloadLargeFile);
document
.querySelector('#cancelDownload')
.addEventListener('click', () => downloadAbortHandler.abort());Conclusion
Use AbortController anywhere you kick off async work that the user might abandon — search inputs, page navigations, file downloads. The pattern is always the same: create a controller, pass signal, call .abort() when you’re done.