Cancellation with AbortController: stopping work nobody needs
Cancel fetch requests and your own async functions with AbortController and AbortSignal. Timeouts with AbortSignal.timeout, combining signals with any, and cleanup in React effects.
Comes up in: JavaScript, Coding, React
Learn#
Rohit’s search box sends a request on every keystroke. Typing “shoes” fires five requests, and they keep downloading after the user has moved on. On a slow 3G connection, the result for “sh” can even arrive last.
The fix is cancellation: telling work that nobody needs its result any more.
An AbortController has one job: controller.abort(). Its signal is an abort signal, which you pass to the work. fetch(url, { signal }) stops the request and rejects with an AbortError when the signal aborts.
function sleep(ms, { signal } = {}) {
return new Promise((resolve, reject) => {
signal?.throwIfAborted();
const id = setTimeout(resolve, ms);
signal?.addEventListener("abort", () => {
clearTimeout(id);
reject(signal.reason);
}, { once: true });
});
}
const controller = new AbortController();
setTimeout(() => controller.abort(), 50);
try { await sleep(1000, { signal: controller.signal }); }
catch (e) { console.log(e.name, controller.signal.aborted); }
try { await sleep(1000, { signal: AbortSignal.timeout(50) }); }
catch (e) { console.log(e.name); }Output (verified)
AbortError true TimeoutError
Network
- GET /search?q=sh …
State
- signal.aborted = false
The search starts a request and passes controller.signal to it.
Read the steps as text
- The search starts a request and passes controller.signal to it.
- The user types again. The code calls controller.abort().
- The signal fires its abort event. fetch stops the request and rejects with an AbortError.
- A new controller is created for the new query. Old results can no longer arrive.
Predict#
Predict first
You call controller.abort() after the fetch promise has already resolved and you have the Response. What happens?
A settled promise never changes. But the body may still be streaming, so response.json() can reject with an AbortError. Abort never undoes work on the server: a sent POST may already have run.
Interview angle#
Say it like this — about 30 seconds
“I create an AbortController and pass its signal to fetch. Calling abort stops the request and fetch rejects with an AbortError, which I ignore in the UI. For timeouts I use AbortSignal.timeout, and AbortSignal.any combines a user signal with a timeout. My own async functions accept a signal, check throwIfAborted, and listen for the abort event to clear timers. In a React effect, the cleanup function aborts the request.”
Follow-up How do you cancel a fetch request?
{ signal: controller.signal } to fetch and call controller.abort(); then handle the AbortError.Follow-up Does aborting a fetch stop the server's work?
Follow-up Can you reuse an AbortController after abort()?
Practice#
On your own
Make sleep abortable, then build fetchWithTimeout from two signals.
Done when
sleeprejects withsignal.reasonwhen aborted, and at once if already aborted.- The timer is cleared, so an aborted sleep never resolves later.
fetchWithTimeoutaborts on the caller’s signal or aftertimeoutms, whichever comes first.
Remember#
Try to answer each card before you open it. Then add them to your reviews.
How do you cancel a fetch?
Pass an AbortController's signal to fetch, then call controller.abort().
What error does fetch reject with after abort()?
A DOMException named AbortError (or the reason you passed).
What error name does AbortSignal.timeout give?
TimeoutError.
What does AbortSignal.any([a, b]) return?
A signal that aborts when any of the given signals aborts.
Go deeper
Four layers. Stop at the one your interview needs; each ends with a quick check.
The core ideaEvery round
abort(reason) sets signal.aborted to true and signal.reason to your reason, or to an AbortError by default. In UI code, check error.name === "AbortError" and do nothing: a cancelled request is not a failure.
Check yourself: What does signal.reason hold after controller.abort()?
A DOMException named AbortError, unless you passed your own reason.
How it works underneathSenior rounds
"abort" and undo your work there (clear timers, close sockets).signal.throwIfAborted() at the start and between steps, so already-cancelled work stops at once Source: MDN: AbortSignal.Check yourself: Why use { once: true } on the abort listener?
The signal aborts once, and the listener is removed after it runs.
Edge cases and trapsWhere people slip
AbortSignal.timeout(ms) aborts with a TimeoutError. AbortSignal.any([a, b]) aborts when any input does Source: DOM Standard: AbortSignal. Support for any: Chrome 116, Firefox 124, Safari 17.4, Node.js 20.3.0.Check yourself: Which static method combines several signals?
AbortSignal.any()
In productionStaff-level answers
return () => controller.abort(). The cleanup runs when inputs change or the component unmounts. In Strict Mode development, where effects run twice, it cancels the first request.Check yourself: What should a React effect's cleanup do with an in-flight fetch?
Call controller.abort(), so the old request stops and its result is ignored.
Finished the five steps? Mark it, and its cards join your reviews.
Last checked 2026-10-31. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.
Interview questions this answers
- How do you cancel a
fetchrequest?