AbortController
JavaScript · Reference cheat sheet
AbortController
JavaScript · Reference cheat sheet
📋 Overview
AbortController and AbortSignal cancel async operations — fetch, streams, and custom tasks. Create a controller, pass signal to APIs, call abort() to reject pending work and run abort listeners. Essential for timeouts, navigation changes, and avoiding race conditions.
🔧 Core concepts
new AbortController()→\{ signal, abort(reason?) \}.signal.aborted,signal.reason,signal.throwIfAborted().signal.addEventListener("abort", ...)— once fired, stays aborted.AbortSignal.timeout(ms): auto-abort after delay.AbortSignal.any([s1, s2]): abort when any aborts (modern).- Fetch:
fetch(url, \{ signal \})rejects withAbortError/TimeoutError.
const c = new AbortController();
fetch("/api", { signal: c.signal }).catch((e) => {
if (e.name === "AbortError") return;
throw e;
});
c.abort();💡 Examples
// Timeout via helper
const data = await fetch("/slow", {
signal: AbortSignal.timeout(5000),
}).then((r) => r.json());
// Manual timeout
const c = new AbortController();
const t = setTimeout(() => c.abort("timeout"), 3000);
try {
await fetch("/api", { signal: c.signal });
} finally {
clearTimeout(t);
}
// Cancel on new request (race guard)
let current;
function search(q) {
current?.abort();
current = new AbortController();
return fetch(`/search?q=${encodeURIComponent(q)}`, {
signal: current.signal,
});
}
// Custom abortable work
function sleep(ms, signal) {
return new Promise((resolve, reject) => {
signal?.throwIfAborted();
const id = setTimeout(resolve, ms);
signal?.addEventListener(
"abort",
() => {
clearTimeout(id);
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
},
{ once: true },
);
});
}
// Combine signals
const userCancel = new AbortController();
const signal = AbortSignal.any([userCancel.signal, AbortSignal.timeout(10_000)]);// Event listener option
el.addEventListener("click", handler, { signal: c.signal });
c.abort(); // removes listener⚠️ Pitfalls
- Aborted fetch throws — always handle
AbortErrorso UI doesn’t treat cancel as failure. - Reusing an already-aborted signal keeps operations aborted — create a new controller per run.
- Not all APIs support signals yet — wrap manually with listeners.
abort(reason)reason support varies; checksignal.reason.- Forgetting to abort on unmount leaks in-flight requests.
🔗 Related
- fetch.md — fetch + signal
- timers.md — timeout patterns
- promise.md — rejection handling
- DOM/events.md — signal option
- event_loop.md — async cancellation