Type fast — watch stale requests cancel
Each keystroke fires a request with random latency. The previous request is aborted the moment you type again — only the final query renders.
Result + Comparison
—
0
Fired
0
Aborted
Without AbortController: stale requests resolve out of order and overwrite fresh data — the user sees results for "r" when they typed "react".
let current = null;
async function search(query) {
current?.abort(); // cancel previous
current = new AbortController();
try {
const res = await fetch(url, {
signal: current.signal,
});
render(await res.json());
} catch (err) {
if (err.name !== 'AbortError')
showError(err); // ignore aborts
}
}
One Controller → Many Requests
All 5 requests share one signal. Calling
abort() once cancels every pending request simultaneously.How It Works
0
Completed
0
Aborted
// ONE controller for a group of requests
const controller = new AbortController();
const signal = controller.signal;
// All share the same signal
const requests = urls.map(url =>
fetch(url, { signal })
);
// Cancel ALL of them at once
controller.abort();
// every pending fetch rejects together
Use case: A dashboard loading 5 widgets. If the user navigates away mid-load, one
abort() cancels all 5 in-flight requests, freeing bandwidth and connections instantly.
AbortSignal.timeout() — Race the Clock
Set a request latency and a timeout. Whichever finishes first wins — the request completes, or the timeout aborts it with a
TimeoutError.
Request takes
2500ms
Timeout at
2000ms
Request
Timeout
Result + Modern Pattern
—
// One line — auto-aborts after 2s
const res = await fetch(url, {
signal: AbortSignal.timeout(2000),
});
// Distinguish WHY it aborted:
catch (err) {
if (err.name === 'TimeoutError')
retry(); // timed out
if (err.name === 'AbortError')
stayQuiet(); // user cancelled
}
Compose signals:
AbortSignal.any([userSignal, AbortSignal.timeout(5000)]) aborts on user action OR timeout — whichever happens first.