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.
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:
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
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.
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.