JavaScript
Promises
Async Programming
Error Handling
Debugging

Terminating hung Promises in javascript

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

A hung Promise is a Promise that stays pending longer than your program can tolerate. The important detail is that you cannot forcibly kill a Promise object itself; you can only stop or ignore the underlying async operation that the Promise represents.

Why Promises Cannot Be “Terminated” Directly

A Promise is just a container for eventual success or failure. Once created, it has no built-in cancellation method. That design keeps Promises simple, but it means cancellation must be designed into the operation around them.

If a fetch call stalls, the Promise does not know how to cancel the network layer by itself. If a timer-based task hangs, the Promise cannot magically clear the timer. Your code has to provide a way to stop the underlying work.

That leads to two practical strategies:

  • cancel the operation when the API supports cancellation
  • stop waiting after a timeout and ignore the late result

The first option is better because it releases resources. The second only protects the caller.

Use AbortController for Supported APIs

Modern web APIs, including fetch, support cancellation through AbortController. You create a controller, pass its signal into the async call, and abort it if the work takes too long.

javascript
1async function loadWithTimeout(url, timeoutMs) {
2  const controller = new AbortController();
3  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
4
5  try {
6    const response = await fetch(url, { signal: controller.signal });
7    if (!response.ok) {
8      throw new Error(`HTTP ${response.status}`);
9    }
10    return await response.json();
11  } finally {
12    clearTimeout(timeoutId);
13  }
14}
15
16loadWithTimeout("https://jsonplaceholder.typicode.com/todos/1", 2000)
17  .then(data => console.log(data))
18  .catch(error => console.error(error.name, error.message));

This is real cancellation. The request is told to stop, and the Promise rejects with an abort-related error.

Promise.race Only Stops Waiting

A common pattern is racing an operation against a timeout Promise.

javascript
1function timeoutAfter(ms) {
2  return new Promise((_, reject) => {
3    setTimeout(() => reject(new Error("Timed out")), ms);
4  });
5}
6
7async function run() {
8  const slowTask = new Promise(resolve => {
9    setTimeout(() => resolve("finished"), 5000);
10  });
11
12  return Promise.race([slowTask, timeoutAfter(1000)]);
13}
14
15run()
16  .then(value => console.log(value))
17  .catch(error => console.error(error.message));

This protects the caller from waiting forever, but it does not cancel slowTask. The timer still runs in the background. That is why Promise.race is a timeout wrapper, not a termination mechanism.

Use it when the operation has no cancellation API, but understand the tradeoff.

Build Cooperative Cancellation Into Your Own Code

For custom async functions, the best design is cooperative cancellation. Accept an AbortSignal or similar token and check it during the operation.

javascript
1function wait(ms, signal) {
2  return new Promise((resolve, reject) => {
3    if (signal.aborted) {
4      reject(new Error("Aborted before start"));
5      return;
6    }
7
8    const id = setTimeout(() => resolve("done"), ms);
9
10    signal.addEventListener("abort", () => {
11      clearTimeout(id);
12      reject(new Error("Operation aborted"));
13    }, { once: true });
14  });
15}
16
17async function demo() {
18  const controller = new AbortController();
19  setTimeout(() => controller.abort(), 500);
20
21  try {
22    const result = await wait(3000, controller.signal);
23    console.log(result);
24  } catch (error) {
25    console.error(error.message);
26  }
27}
28
29demo();

This pattern scales well. It works for timers, polling loops, stream processing, and long-running workflows because the async code knows how to clean up after itself.

Design for Cleanup, Not Just Failure

Hung async work is often more than a user-facing timeout problem. It can leave open sockets, active intervals, progress indicators, or stale state updates. When you add cancellation, include cleanup logic.

For example, if an operation updates UI state when it completes, guard against late completion after the user has navigated away. If a timeout causes the caller to move on, the eventual result should not still mutate state that is no longer relevant.

A useful rule is to separate these concerns:

  • timeout decides how long the caller waits
  • cancellation stops underlying work
  • cleanup releases resources and prevents stale updates

When those three concerns are handled explicitly, async code becomes much easier to debug.

Common Pitfalls

Assuming Promise.race cancels the losing Promise is the most common mistake. It does not.

Aborting the caller without aborting the underlying work can still leak resources. If the API supports AbortSignal, use it.

Ignoring cleanup is another frequent problem. Clear timers, detach listeners, and prevent late state updates after cancellation.

Creating custom cancellation flags without a consistent contract also causes bugs. Prefer AbortController unless you have a strong reason to invent your own pattern.

Summary

  • a Promise cannot be forcibly terminated by itself
  • real cancellation requires support from the underlying async operation
  • 'AbortController is the standard solution for cancellable web APIs'
  • 'Promise.race adds a timeout but does not stop the losing task'
  • cooperative cancellation and cleanup are the key to reliable async code

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

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

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.