Skip to content
easy-cancelable-promise

Converting native promises

Turn a Promise, a value or a function into a CancelablePromise with toCancelablePromise, know what a wrapper can and cannot stop, and check what you have with the type guards.

GuideTypeScriptTry an example

toCancelablePromise

toCancelablePromise accepts anything and returns a CancelablePromise.

You passYou get
A CancelablePromiseThe same instance
A native PromiseA cancelable promise that follows it
A functionThe function is called, and its result is converted
Any other valueA cancelable promise already resolved with that value
to-cancelable.ts
import { CancelablePromise, toCancelablePromise } from 'easy-cancelable-promise';

export async function convert() {
  // from a native promise
  const fromPromise = toCancelablePromise(Promise.resolve(1));

  // from a plain value
  const fromValue = toCancelablePromise(2);

  // from a function: it is called, and its result is converted
  const fromFunction = toCancelablePromise(() => Promise.resolve(3));

  // a CancelablePromise is returned as it is
  const original = new CancelablePromise<number>((resolve) => resolve(4));
  const same = toCancelablePromise(original);

  // each one is a CancelablePromise<number>
  const doubled = fromPromise.then((value) => value * 2);

  return {
    values: await Promise.all([fromPromise, fromValue, fromFunction, same]), // [1, 2, 3, 4]
    doubled: await doubled, // 2
    sameInstance: same === original, // true
  };
}

The type of the result is inferred from what you pass, so then and await receive the right type.

What a wrapper can stop

A native promise has no way to be stopped. Wrapping it gives you cancel, status and onCancel, and whoever awaits the wrapper stops waiting. The work behind the native promise still runs to the end.

limits.ts
import { toCancelablePromise } from 'easy-cancelable-promise';

export async function limits() {
  let finished = false;

  const native = new Promise<string>((resolve) =>
    setTimeout(() => {
      finished = true;
      resolve('The work was done anyway');
    }, 20),
  );

  const cancelable = toCancelablePromise(native);

  // the wrapper is canceled: whoever awaits it stops waiting
  cancelable.cancel('Not needed');

  await cancelable.catch(() => {});
  await native;

  // the native promise had no way to stop, so its work ran to the end
  return [cancelable.status, finished]; // ['canceled', true]
}

Stop the work itself

To stop the work, create the CancelablePromise where the work starts, and connect onCancel to whatever can stop it. For fetch that is an AbortController.

wrap-fetch.ts
import { CancelablePromise } from 'easy-cancelable-promise';

/** To stop the work itself, connect the cancellation to whatever can stop it. */
export const cancelableFetch = (input: RequestInfo | URL, init: RequestInit = {}) =>
  new CancelablePromise<Response>((resolve, reject, { onCancel }) => {
    const controller = new AbortController();

    onCancel(() => controller.abort());

    fetch(input, { ...init, signal: controller.signal }).then(resolve, reject);
  });

The same shape works for timers (clearTimeout), sockets (close), streams (cancel) and other libraries that accept an AbortSignal.

Type guards

isPromise and isCancelablePromise tell you what a value is at runtime, and narrow its type.

guards.ts
import { CancelablePromise, isCancelablePromise, isPromise, toCancelablePromise } from 'easy-cancelable-promise';

export function describe(value: unknown) {
  if (isCancelablePromise<string>(value)) {
    // value is a CancelablePromise<string> here
    return `cancelable, ${value.status}`;
  }

  if (isPromise(value)) {
    // value is a Promise<unknown> here: anything with a then method
    return 'promise';
  }

  return 'value';
}

export const examples = [
  describe(new CancelablePromise<string>(() => {})), // 'cancelable, pending'
  describe(Promise.resolve()), // 'promise'
  describe({ then: () => {} }), // 'promise'
  describe(42), // 'value'
];

/** Accepts anything and always works with a CancelablePromise. */
export const normalize = (value: unknown) => toCancelablePromise(value);
  • isPromise is true for anything with a then method.
  • isCancelablePromise is true only for instances of CancelablePromise. It also recognizes promises created by another copy of the package, which instanceof does not. See Troubleshooting.

Next

Documentation
↑↓ Navigate ↵ Open Esc Closeeasy-cancelable-promise docs
Get started

Promises you can actually control.