Fetch: Abort
Sometimes you need to cancel an in-progress fetch β user navigates away, search input changes, or a request takes too long. AbortController makes this possible.
AbortController Basics
const controller = new AbortController();
const signal = controller.signal;
// Pass signal to fetch
fetch(url, { signal });
// Later, cancel the request
controller.abort();
When abort() is called, the fetch promise rejects with an AbortError.
Demo: Abort a Fetch Request
HTML
<div>
<div style='display:flex;gap:8px;margin-bottom:8px;'>
<button id='start-fetch'>Start Long Request</button>
<button id='abort-fetch'>Abort</button>
</div>
<pre id='abort-out' style='background:#f1f5f9;padding:12px;border-radius:6px;min-height:60px;'></pre>
</div> JavaScript
const out = document.getElementById('abort-out');
let controller = null;
document.getElementById('start-fetch').onclick = async function() {
controller = new AbortController();
this.disabled = true;
out.textContent = 'Request started...\\n';
try {
// Simulate a slow endpoint by using a large delay
const response = await fetch('https://jsonplaceholder.typicode.com/posts?_delay=5000', {
signal: controller.signal
});
const data = await response.json();
out.textContent += 'Received ' + data.length + ' items';
} catch (err) {
if (err.name === 'AbortError') {
out.textContent += 'Request was aborted!';
} else {
out.textContent += 'Error: ' + err.message;
}
} finally {
this.disabled = false;
controller = null;
}
};
document.getElementById('abort-fetch').onclick = function() {
if (controller) {
controller.abort();
out.textContent += 'Abort signal sent at ' + new Date().toLocaleTimeString() + '\\n';
} else {
out.textContent = 'No active request to abort';
}
}; Live Output Window
AbortError Detection
When a request is aborted, the promise rejects with a DOMException named AbortError:
try {
const response = await fetch(url, { signal });
} catch (err) {
if (err.name === 'AbortError') {
// User cancelled β do nothing
return;
}
// Real error β handle it
console.error(err);
}
Aborting Multiple Requests
One AbortController can abort multiple requests at once:
const controller = new AbortController();
const signal = controller.signal;
const [users, posts] = await Promise.all([
fetch('/api/users', { signal }),
fetch('/api/posts', { signal })
]);
// Abort both if one fails
Timeout with AbortController
Combine with setTimeout to implement request timeouts:
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
// process response
} catch (err) {
if (err.name === 'AbortError') {
console.log('Request timed out');
}
}
Demo: Fetch with Timeout
HTML
<div>
<button id='timeout-btn'>Fetch with 3s Timeout</button>
<pre id='timeout-out' style='background:#f1f5f9;padding:12px;border-radius:6px;min-height:60px;'></pre>
</div> JavaScript
const out = document.getElementById('timeout-out');
document.getElementById('timeout-btn').onclick = async function() {
this.disabled = true;
out.textContent = 'Fetching with 3s timeout...\\n';
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
out.textContent += 'β±οΈ Timeout reached! Aborting...\\n';
}, 3000);
try {
// Use a URL that may be slow
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1', {
signal: controller.signal
});
clearTimeout(timeoutId);
const data = await response.json();
out.textContent += 'β
Received: ' + data.title;
} catch (err) {
if (err.name === 'AbortError') {
out.textContent += 'β Request aborted (timeout or cancelled)';
} else {
out.textContent += 'β Error: ' + err.message;
}
} finally {
this.disabled = false;
}
}; Live Output Window
Cleanup in React/Components
Abort fetch when a component unmounts:
useEffect(() => {
const controller = new AbortController();
async function loadData() {
try {
const res = await fetch('/api/data', { signal: controller.signal });
const data = await res.json();
setData(data);
} catch (err) {
if (err.name !== 'AbortError') setError(err);
}
}
loadData();
return () => controller.abort(); // cleanup on unmount
}, []);
AbortController for Non-Fetch
AbortController isnβt limited to fetch β you can use it for any async operation:
function wait(ms, { signal } = {}) {
return new Promise((resolve, reject) => {
const timer = setTimeout(resolve, ms);
if (signal) {
signal.addEventListener('abort', () => {
clearTimeout(timer);
reject(new DOMException('Aborted', 'AbortError'));
});
}
});
}
// Usage
const controller = new AbortController();
await wait(5000, { signal: controller.signal });
Key Takeaways
AbortController+signalcancels fetch requests- Catch
AbortErrorspecifically to distinguish cancellation from real errors - One
AbortControllercan cancel multiple requests - Combine with
setTimeoutfor request timeouts - Always clean up β call
abort()when component unmounts - Check
signal.abortedto see if abort has already been called - AbortController works with any async operation, not just fetch