Glossary

AbortController

AbortController is a DOM interface that lets you cancel ongoing asynchronous work, such as a fetch request, by calling abort() on the controller. It is defined in the WHATWG DOM Standard. Each controller owns one AbortSignal, which you pass to the API you want to be able to cancel. The detail people search for is the error name: AbortError for a manual abort and TimeoutError for a timeout.

How it works

A controller has one property, signal, and one method, abort(reason). Calling abort() flips the signal to aborted, stores the reason in signal.reason, and fires an abort event on the signal. Promise-based APIs that accept the signal then reject with that reason.

If you call abort() with no argument, the reason is a new DOMException named "AbortError" (legacy code 20). If you pass a value, that exact value becomes the reason and is what the rejected promise carries. Calling abort() a second time does nothing, because the signal is already aborted and keeps its first reason.

The signal also has helpers:

  • signal.aborted is true once aborted.
  • signal.throwIfAborted() throws signal.reason if the signal is aborted, so you can check at the start of your own async function.
  • AbortSignal.abort(reason) returns a signal that is already aborted.
  • AbortSignal.timeout(ms) returns a signal that aborts after ms milliseconds with a "TimeoutError" DOMException (code 23).
  • AbortSignal.any(signals) returns a signal that aborts as soon as any signal in the list does.
const http = require('http');
const srv = http.createServer((req, res) => { setTimeout(() => res.end('late'), 2000); }).listen(0, async () => {
  const url = 'http://127.0.0.1:' + srv.address().port;
  const c = new AbortController();
  setTimeout(() => c.abort(), 50);
  try { await fetch(url, { signal: c.signal }); } catch (e) { console.log(e.name, e.code, e.message, c.signal.aborted); }
  try { await fetch(url, { signal: AbortSignal.timeout(50) }); } catch (e) { console.log(e.name, e.code, e.message); }
  const c2 = new AbortController(); c2.abort('stop it');
  console.log(c2.signal.reason, c2.signal.aborted);
  const c3 = new AbortController(); c3.abort();
  console.log(c3.signal.reason.name, c3.signal.reason.code);
  c3.abort('again'); console.log(c3.signal.reason.name);
  const any = AbortSignal.any([new AbortController().signal, AbortSignal.abort('x')]);
  console.log(any.aborted, any.reason);
  srv.close(); srv.closeAllConnections();
});

Output from Node 22.22.0:

AbortError 20 This operation was aborted true
TimeoutError 23 The operation was aborted due to timeout
stop it true
AbortError 20
AbortError
true x

How do I cancel a fetch request?

Create a controller, pass controller.signal in the fetch init object, and call controller.abort() when you want to stop. The fetch promise rejects with the abort reason, and the browser stops reading the response. In a catch block, compare err.name with the string AbortError to tell a cancel apart from a real failure.

Can I reuse an AbortController?

No. Once aborted, a signal stays aborted for good, and any new request that receives it rejects immediately. Create a fresh AbortController for every operation, for instance one per search keystroke or per component mount.

Common pitfalls

  • Reusing a spent controller: every later fetch with the same signal fails at once. Make a new controller per request.
  • Treating AbortError as a failure: a cancel is intentional, so log or show an error only when err.name is not "AbortError" and not "TimeoutError".
  • Passing a custom reason and checking the name: abort('stop it') makes the rejection value the string, which has no name property. Use a DOMException or Error if callers inspect it.
  • Assuming the server stops working: aborting closes the client side only. A POST that already reached the server may still be processed.
  • Hand-rolled timeouts: a setTimeout that calls abort() works, but you must clear it on success. AbortSignal.timeout(ms) needs no cleanup.
  • Aborting after the body is read: abort() has no effect on a promise that already settled.

Related terms

  • Fetch API — the most common consumer of an AbortSignal.
  • Promise — abort rejects the pending promise with the reason.
  • Async/await — wrap the awaited call in try and catch to handle the abort.
  • DOM — the standard that defines AbortController and event targets.
  • Event loop — the timeout fires as a task on the timer task source.

See also

  • Term: Fetch API — the request interface you most often cancel.