AbortController

new AbortController()
Returns: AbortController · Added in vDOM Living Standard · Updated March 13, 2026 · Built-in Objects
async fetch abort controller

The AbortController interface represents a controller object that allows you to abort one or more DOM requests as and when required. You can signal and abort asynchronous operations like fetch requests, enabling you to implement cancellation functionality for network requests, timeouts, and other asynchronous tasks. When abort() is called on an AbortController, it triggers the abort event on the associated AbortSignal, which causes any DOM operation using that signal to throw an AbortError. This API is essential for building responsive applications where users need the ability to cancel pending operations—preventing wasted bandwidth, reducing server load, and improving user experience by eliminating zombie requests that no longer matter.

Syntax

new AbortController()
// TypeScript
const controller: AbortController = new AbortController();
const signal: AbortSignal = controller.signal;

Properties

PropertyTypeDescription
signalAbortSignalReturns an AbortSignal object instance, which can be used to communicate with/abort an asynchronous operation.

Methods

MethodParametersDescription
abort()reason?: anyInvokes the abort process, triggering the abort event on the associated AbortSignal. Optional reason parameter available in modern browsers.

Examples

Basic Fetch with Abort

const controller = new AbortController();
const signal = controller.signal;
fetch('https://jsonplaceholder.typicode.com/posts/1', { signal })
  .then(response => response.json())
  .then(data => console.log('Data received:', data))
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Fetch aborted');
    } else {
      console.error('Fetch error:', error);
    }
  });
// Abort the request after 1 second
setTimeout(() => controller.abort(), 1000);
// Output (after 1 second):
// Fetch aborted

Implementing Request Timeout

function fetchWithTimeout(url, timeout = 3000) {
  const controller = new AbortController();
  const id = setTimeout(() => controller.abort(), timeout);
  
  return fetch(url, { signal: controller.signal })
    .then(response => {
      clearTimeout(id);
      return response;
    })
    .catch(error => {
      clearTimeout(id);
      if (error.name === 'AbortError') {
        throw new Error(`Request timed out after ${timeout}ms`);
      }
      throw error;
    });
}
fetchWithTimeout('https://jsonplaceholder.typicode.com/users/1', 2000)
  .then(r => r.json())
  .then(console.log)
  .catch(err => console.error(err.message));
// Output (if request takes > 2 seconds):
// Request timed out after 2000ms

Using AbortController with async/await

async function fetchUserData(userId, signal) {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/users/${userId}`,
    { signal }
  );
  
  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }
  
  return response.json();
}
async function loadUserWithCancel() {
  const controller = new AbortController();
  
  const loadPromise = fetchUserData(1, controller.signal);
  
  // Cancel after 500ms
  setTimeout(() => controller.abort(), 500);
  
  try {
    const user = await loadPromise;
    console.log('User loaded:', user.name);
  } catch (error) {
    if (error.name === 'AbortError') {
      console.log('Operation cancelled by user');
    } else {
      console.error('Error:', error.message);
    }
  }
}
loadUserWithCancel();
// Output:
// Operation cancelled by user

Aborting Multiple Concurrent Requests

async function fetchMultipleUrls(urls) {
  const controller = new AbortController();
  
  const fetchPromises = urls.map(url => 
    fetch(url, { signal: controller.signal })
      .then(r => r.json())
  );
  
  // Abort all if any request takes too long
  const timeoutId = setTimeout(() => {
    console.log('Request timed out, aborting all...');
    controller.abort();
  }, 3000);
  
  try {
    const results = await Promise.allSettled(fetchPromises);
    clearTimeout(timeoutId);
    
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`URL ${urls[index]} succeeded:`, result.value);
      } else {
        console.log(`URL ${urls[index]} failed:`, result.reason.name);
      }
    });
  } catch (error) {
    console.error('All requests aborted');
  }
}
fetchMultipleUrls([
  'https://jsonplaceholder.typicode.com/users/1',
  'https://jsonplaceholder.typicode.com/users/2'
]);
// Output:
// URL https://jsonplaceholder.typicode.com/users/1 succeeded: { id: 1, name: 'Leanne Graham', ... }
// URL https://jsonplaceholder.typicode.com/users/2 succeeded: { id: 2, name: 'Ervin Howell', ... }

Listening to Abort Events

const controller = new AbortController();
const { signal } = controller;
signal.addEventListener('abort', () => {
  console.log('Operation has been aborted');
});
signal.addEventListener('abort', () => {
  console.log('Cleanup completed');
});
// Trigger abort
controller.abort();
// Output:
// Operation has been aborted
// Cleanup completed

Common Patterns

Cancellation Token Pattern

function createCancellableOperation() {
  const controller = new AbortController();
  
  const operation = async () => {
    const response = await fetch('/api/data', { signal: controller.signal });
    return response.json();
  };
  
  return {
    execute: operation,
    cancel: () => controller.abort(),
    signal: controller.signal
  };
}
const { execute, cancel, signal } = createCancellableOperation();
// Later: cancel();

Debounced Search with Abort

async function search(query, signal) {
  const response = await fetch(`/api/search?q=${query}`, { signal });
  return response.json();
}
function debouncedSearch(query, delay = 300) {
  let controller;
  let timeoutId;
  
  return new Promise((resolve, reject) => {
    // Cancel previous request
    if (controller) controller.abort();
    
    controller = new AbortController();
    
    clearTimeout(timeoutId);
    timeoutId = setTimeout(async () => {
      try {
        const results = await search(query, controller.signal);
        resolve(results);
      } catch (error) {
        if (error.name !== 'AbortError') reject(error);
      }
    }, delay);
  });
}

Abort Reason (Modern Browsers)

const controller = new AbortController();
fetch('/api/data', { signal: controller.signal })
  .catch(error => {
    console.log('Abort reason:', error.reason); // "User clicked cancel"
    console.log('Error name:', error.name);      // "AbortError"
  });
controller.abort('User clicked cancel');

See Also

  • Fetch:: — HTTP requests with abort signal support
  • AbortSignal — The signal object for abort communication
  • promise:: — Underlying promise handling for async operations
  • Fetch:: — Request object that accepts abort signals