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.
toCancelablePromise
toCancelablePromise accepts anything and returns a CancelablePromise.
| You pass | You get |
|---|---|
A CancelablePromise | The same instance |
A native Promise | A cancelable promise that follows it |
| A function | The function is called, and its result is converted |
| Any other value | A cancelable promise already resolved with that value |
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.
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.
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.
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);isPromiseistruefor anything with athenmethod.isCancelablePromiseistrueonly for instances ofCancelablePromise. It also recognizes promises created by another copy of the package, whichinstanceofdoes not. See Troubleshooting.