Asynchronous programming
JavaScript
Promise
Async/Await
API cancellation

How to cancel an asynchronous call?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

You cannot cancel every asynchronous operation the same way, because cancellation only works when the underlying API supports it. In modern JavaScript, the usual answer is AbortController for APIs such as fetch, plus explicit cancellation logic for timers, streams, or custom async workflows.

Promises Themselves Are Not Cancellable

A Promise represents a future result, but the Promise object itself does not provide a built-in cancel method.

This means code like this is not a thing in standard JavaScript:

javascript
const promise = fetch('/api/data');
// promise.cancel()  // not part of standard Promise

To cancel work, you need an async API that understands cancellation and exposes a control mechanism for it.

Use AbortController With fetch

The standard pattern for network requests is:

javascript
1const controller = new AbortController();
2const signal = controller.signal;
3
4fetch('https://example.com/data', { signal })
5  .then(response => response.json())
6  .then(data => console.log(data))
7  .catch(err => {
8    if (err.name === 'AbortError') {
9      console.log('Request was cancelled');
10    } else {
11      console.error(err);
12    }
13  });
14
15controller.abort();

That works because fetch is designed to respect the abort signal.

This is the most common real-world answer for browser and modern Node.js HTTP request cancellation.

Async/Await Uses The Same Mechanism

With async and await, the cancellation mechanism does not change:

javascript
1async function loadUser(signal) {
2  const response = await fetch('https://example.com/user', { signal });
3  return response.json();
4}
5
6const controller = new AbortController();
7
8loadUser(controller.signal)
9  .then(user => console.log(user))
10  .catch(err => {
11    if (err.name === 'AbortError') {
12      console.log('Cancelled');
13    }
14  });
15
16controller.abort();

async and await change the syntax, not the cancellation model.

Timers And Custom Work Need Different Logic

For timers, use the matching timer API:

javascript
1const id = setTimeout(() => {
2  console.log('This will not run');
3}, 5000);
4
5clearTimeout(id);

For custom async functions, you often pass an AbortSignal and check it yourself:

javascript
1function delay(ms, signal) {
2  return new Promise((resolve, reject) => {
3    const id = setTimeout(resolve, ms);
4
5    signal.addEventListener('abort', () => {
6      clearTimeout(id);
7      reject(new DOMException('Aborted', 'AbortError'));
8    }, { once: true });
9  });
10}

This is how you make your own async code cancellable instead of only the built-in browser APIs.

Sometimes The Right Move Is To Ignore Stale Results

In UI code, true cancellation is not always necessary. Sometimes the practical goal is just to ignore results from an outdated request:

javascript
1let latestRequestId = 0;
2
3async function search(query) {
4  const requestId = ++latestRequestId;
5  const response = await fetch(`/search?q=${encodeURIComponent(query)}`);
6  const data = await response.json();
7
8  if (requestId !== latestRequestId) {
9    return;
10  }
11
12  renderResults(data);
13}

This is common in typeahead search, where the user keeps typing and older responses should not overwrite newer ones.

That is not the same as cancelling the network operation, but it often solves the actual UI problem.

In practice, many responsive interfaces use both techniques together: cancel what can be cancelled, and still guard against stale responses in case cancellation arrives too late or the underlying API ignores it.

Common Pitfalls

One common mistake is assuming Promise objects are cancellable by default.

Another issue is calling abort() without handling the resulting AbortError, which turns expected cancellation into noisy error logging.

A third problem is forgetting that cancellation only works if the underlying API cooperates. Wrapping a non-cancellable operation in a Promise does not magically make it cancellable.

Finally, some interfaces need stale-result protection more than true cancellation, especially in search and navigation-heavy UIs.

Summary

  • Standard Promises are not cancellable by themselves.
  • Use AbortController when the async API supports AbortSignal, especially with fetch.
  • Use the matching cancel API for timers, streams, or custom async work.
  • 'async and await do not change how cancellation works.'
  • In some UI flows, ignoring stale results is more important than truly stopping the underlying request.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.