How to cancel an asynchronous operation? (AbortController)

👨‍💻 Frontend Developer 🟠 May come up 🎚️ Medium
#JavaScript #Asynchronicity #JS Basics

Brief Answer

AbortController is a built-in JavaScript mechanism for cancelling asynchronous operations. It allows creating a cancellation signal that can be passed to asynchronous operations (fetch, setTimeout, etc.) to interrupt their execution. This is especially useful for cancelling HTTP requests, timers and other long-running operations.

Main components:

  • AbortController — creates cancellation controller
  • signal — cancellation signal passed to operations
  • abort() — method to initiate cancellation

Full Answer

AbortController is a standard API in JavaScript designed to cancel asynchronous operations. It emerged as a solution to the problem of lacking a universal way to cancel promises and asynchronous operations in the language.

How AbortController Works

AbortController creates a cancellation signal that can be passed to asynchronous operations:

const controller = new AbortController();
const { signal } = controller;
 
// Pass signal to asynchronous operation
fetch('/api/data', { signal });
 
// Cancel operation
controller.abort();

Main Use Cases

1. Cancelling HTTP Requests

The most common use case:

const controller = new AbortController();
 
fetch('/api/long-operation', { 
  signal: controller.signal 
})
.then(response => response.json())
.catch(error => {
  if (error.name === 'AbortError') {
    console.log('Request cancelled');
  }
});
 
// Cancel request
controller.abort();

2. Cancelling Timers

Timers can be cancelled using AbortController:

function delay(ms, signal) {
  return new Promise((resolve, reject) => {
    const timeoutId = setTimeout(resolve, ms);
    
    // Listen for cancellation signal
    signal.addEventListener('abort', () => {
      clearTimeout(timeoutId);
      reject(new DOMException('Operation cancelled', 'AbortError'));
    });
  });
}

Practical Examples

Cancelling Request on Timeout

function fetchWithTimeout(url, timeoutMs) {
  const controller = new AbortController();
  
  // Cancel on timeout
  const timeoutId = setTimeout(() => {
    controller.abort();
  }, timeoutMs);
  
  return fetch(url, { signal: controller.signal })
    .finally(() => clearTimeout(timeoutId));
}

Cancelling on Page Navigation

const controller = new AbortController();
 
// Cancel all requests on navigation
window.addEventListener('beforeunload', () => {
  controller.abort();
});
 
fetch('/api/data', { signal: controller.signal });

Working with Cancellation Signal

Checking Signal State

const controller = new AbortController();
const { signal } = controller;
 
// Check if signal is cancelled
if (signal.aborted) {
  console.log('Operation already cancelled');
}

Listening for Cancellation Event

signal.addEventListener('abort', () => {
  console.log('Operation cancelled');
});

Common Mistakes and Solutions

1. Ignoring Cancellation Errors

// ❌ Ignoring cancellation errors
fetch('/api/data', { signal })
  .then(response => console.log(response));
 
// ✅ Properly handling errors
fetch('/api/data', { signal })
  .then(response => console.log(response))
  .catch(error => {
    if (error.name === 'AbortError') {
      // Expected cancellation, not an error
      console.log('Request cancelled');
    } else {
      // Real error
      console.error('Request error:', error);
    }
  });

2. Cancelling Already Completed Operations

Calling abort() on already completed operations doesn’t cause errors but has no effect.

Best Practices

  1. Always handle AbortError — this is expected behavior, not an error
  2. Use one controller for related operations — for group cancellation
  3. Clean up timers on cancellation — avoid memory leaks
  4. Don’t reuse AbortController — create new one for each operation
  5. Cancel operations when user leaves — improves performance

Compatibility

AbortController is supported by all modern browsers. Polyfill required for older browsers.

Key AbortController Benefits

  1. Standardized approach — unified way to cancel all asynchronous operations
  2. Versatility — works with fetch, timers and custom operations
  3. Ease of use — intuitive API
  4. Resource efficiency — prevents execution of unnecessary operations

AbortController is a powerful tool for managing asynchronous operations, allowing efficient cancellation of requests and resource freeing.


Knowledge Check Task

Task

What will happen when executing this code and how to improve it?

const controller = new AbortController();
 
fetch('/api/data', { signal: controller.signal })
  .then(response => response.json())
  .then(data => console.log(data));
 
// Cancel after 2 seconds
setTimeout(() => {
  controller.abort();
}, 2000);
View answer

Answer: When executing this code:

  1. HTTP request to /api/data will start
  2. After 2 seconds controller.abort() will be called
  3. If request isn’t completed yet, it will be cancelled
  4. But code lacks cancellation error handling, so unhandled error will occur

Improved version:

const controller = new AbortController();
 
fetch('/api/data', { signal: controller.signal })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Request was cancelled');
    } else {
      console.error('Request error:', error);
    }
  });
 
// Cancel after 2 seconds
setTimeout(() => {
  controller.abort();
}, 2000);

Explanation:

  • When fetch is cancelled, it throws an error with name “AbortError”
  • Without catch handling, this error remains unhandled
  • Proper handling allows distinguishing cancellation from real errors
  • User gets clear message that request was cancelled

It’s important to understand that AbortController is not instant cancellation, but a signal that asynchronous operations can check and respond to appropriately.


Want more articles to prepare for interviews? Subscribe to EasyAdvice, bookmark the site and improve yourself every day 💪