How to Annul Promises in JavaScript

Creating Cancelable Tasks in JavaScript with Promise.withResolvers and AbortController

In JavaScript, you might already know how to cancel a request: you can use xhr.abort() for XHR and signal for fetch. But how do you cancel a regular Promise?

Currently, JavaScript's Promise does not natively provide an API to cancel a regular Promise. So, what we’ll discuss next is how to discard/ignore the result of a Promise.

Method 1: Using the New Promise.withResolvers()

A new API that can now be used is Promise.withResolvers(). It returns an object containing a new Promise object and two functions to resolve or reject it.

Here’s how the code looks:

let resolve, reject;
const promise = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});

Now we can do this:

const { promise, resolve, reject } = Promise.withResolvers();

So we can utilize this to expose a cancel method:

const buildCancelableTask = <T>(asyncFn: () => Promise<T>) => {
  let rejected = false;
  const { promise, resolve, reject } = Promise.withResolvers<T>();

  return {
    run: () => {
      if (!rejected) {
        asyncFn().then(resolve, reject);
      }

      return promise;
    },

    cancel: () => {
      rejected = true;
      reject(new Error('CanceledError'));
    },
  };
};

Then we can use it with the following test code:

const sleep = (ms: number) => new Promise(res => setTimeout(res, ms));

const ret = buildCancelableTask(async () => {
  await sleep(1000);
  return 'Hello';
});

(async () => {
  try {
    const val = await ret.run();
    console.log('val: ', val);
  } catch (err) {
    console.log('err: ', err);
  }
})();

setTimeout(() => {
  ret.cancel();
}, 500);

Here, we preset the task to take at least 1000ms, but we cancel it within the next 500ms, so you will see:

Note that this is not true cancellation but an early rejection. The original asyncFn() will continue to execute until it resolves or rejects, but it doesn’t matter because the promise created with Promise.withResolvers<T>() has already been rejected.

Method 2: Using AbortController

Subscribe to keep reading

This content is free, but you must be subscribed to Web Developer to continue reading.

Already a subscriber?Sign In.Not now

Reply

or to participate.