Skip to main content

AbortController in JavaScript

AbortController is a special object that lets you create a "cancellation signal" (AbortSignal) and send a "cancel" command to asynchronous operations that listen to it. Put simply: AbortController manages the "cancelled or not" state, while AbortSignal is the flag that asynchronous code can watch.

Theory

TL;DR

  • new AbortController() creates a controller with two key members: signal and abort().
  • controller.signal is an AbortSignal object that you pass into an asynchronous operation.
  • controller.abort() flips the signal into the cancelled state and fires all of its listeners.
  • In fetch, an aborted request rejects with an error whose err.name === 'AbortError'.
  • The signal can also be supported in your own asynchronous functions: read signal.aborted and listen for the abort event.
  • A controller is single use: the next operation needs a new instance.

Quick example

javascript
const controller = new AbortController(); // create the controller const signal = controller.signal; // take the signal out of it fetch('https://example.com/data', { signal }) .then(response => response.json()) .then(data => console.log(data)) .catch(err => { if (err.name === 'AbortError') { console.log('The request was aborted'); } else { console.error('Error:', err); } }); // Abort the request after 1 second: setTimeout(() => { controller.abort(); // cancellation }, 1000);

What happens:

  1. We create the controller, then pass controller.signal into fetch.
  2. When we call controller.abort(), fetch is interrupted instantly and throws an AbortError.

How it works under the hood

AbortController creates an object with two key properties:

  • signal, an AbortSignal object that can be passed into an asynchronous operation;
  • abort(), a method that activates that signal: it changes the signal's state and calls every registered handler.

The controller itself cannot cancel anything. It only flips a flag, and the actual interruption is done by whichever API you handed the signal to.

What AbortSignal can do

AbortSignal:

  • has an .aborted property (a boolean: true if the operation was cancelled);
  • can listen for the "abort" event.
javascript
const controller = new AbortController(); const signal = controller.signal; signal.addEventListener('abort', () => { console.log('The operation was cancelled!'); }); controller.abort(); // => "The operation was cancelled!"

Where you can use it

AbortController is supported by many APIs:

APISupports cancellation?
fetch()Yes
ReadableStream, WritableStreamYes
WebSocket (through your own wrapper)Partially
setTimeout / setInterval (through the AbortSignal API in Node 20+ or a browser polyfill)Yes
navigator.sendBeacon() (in some browsers)Partially
Your own async functionsYes, if you implement the support by hand

Example with a Promise and custom cancellation

You can use the signal inside your own asynchronous functions:

javascript
function wait(ms, signal) { return new Promise((resolve, reject) => { const timeout = setTimeout(resolve, ms); signal.addEventListener('abort', () => { clearTimeout(timeout); reject(new Error('Cancelled by the user')); }); }); } const controller = new AbortController(); wait(5000, controller.signal) .then(() => console.log('5 seconds have passed')) .catch(err => console.log(err.message)); setTimeout(() => controller.abort(), 2000); // cancel after 2 seconds

Result:

text
Cancelled by the user

Reuse and the API summary

A controller is single use:

  • after controller.abort() its signal gets .aborted = true forever;
  • to cancel again you have to create a new AbortController.
Property or methodWhat it does
new AbortController()creates a controller
.signalthe signal to pass into an async API
.abort()sends the "cancel" command
signal.abortedtrue if cancelled
signal.addEventListener('abort', fn)listens for the cancellation event

Common mistakes

  • Reusing one controller for several requests. After the first abort() the signal is already cancelled, so the next fetch using it fails immediately. Create a new controller per request.
  • Swallowing AbortError like an ordinary failure. In catch you must check err.name === 'AbortError' separately, otherwise the user sees a failure message where the request was cancelled on purpose.
  • Expecting abort() to cancel the work on the server. Only the client side is cancelled: the browser tears down the connection, but the server may have already performed the operation.
  • Forgetting to remove the abort listener. In your own functions the listener should be removed once the work finishes (for example with { once: true }), otherwise a long lived signal keeps a reference alive and leaks memory.
  • Ignoring an already aborted signal. If you write your own async function, check signal.aborted at the start, because the signal may have been aborted before the function was even called.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.