Getting started
Install easy-cancelable-promise, create your first CancelablePromise, cancel it, clean up after it and report its progress.
Install
npm install easy-cancelable-promiseThe package has no dependencies, works with any framework or none, and ships its own TypeScript types.
Create a CancelablePromise
A CancelablePromise is created like a Promise. The callback receives resolve and reject, and a third
parameter with the utilities of the promise. onCancel is the one you will use most: it registers what has to
happen when the promise is canceled.
import { CancelablePromise } from 'easy-cancelable-promise';
export const wait = (milliseconds: number) =>
new CancelablePromise<string>((resolve, _reject, { onCancel }) => {
const timer = setTimeout(() => resolve('Done!'), milliseconds);
// runs only if the promise is canceled: this is where the work is stopped
onCancel(() => clearTimeout(timer));
});Without onCancel the promise would still be canceled, but the timer would keep running. Canceling a promise
tells everyone to stop waiting. onCancel is where the work itself is stopped.
Use it like a Promise
CancelablePromise extends Promise. Everything that accepts a promise accepts it: await, then, catch,
finally, Promise.all. On top of that it has a status and a cancel method.
import { wait } from './first-promise';
export async function example() {
// it is a Promise: await it, chain it, pass it to anything that takes a promise
const result = await wait(100);
console.log(result); // 'Done!'
const pending = wait(5000);
console.log(pending.status); // 'pending'
pending.cancel('No longer needed');
console.log(pending.status); // 'canceled'
// a canceled promise rejects with the reason of the cancellation
await pending.catch((reason) => {
console.log(reason); // 'No longer needed'
});
return result;
}Cancel a real request
The same pattern works for anything that can be stopped. For fetch, that is an AbortController.
import { CancelablePromise } from 'easy-cancelable-promise';
export interface User {
id: number;
name: string;
}
export const fetchUser = (id: number) =>
new CancelablePromise<User>((resolve, reject, { onCancel }) => {
const controller = new AbortController();
// canceling the promise aborts the request
onCancel(() => controller.abort());
fetch(`/api/users/${id}`, { signal: controller.signal })
.then((response) => response.json() as Promise<User>)
.then(resolve, reject);
});
const request = fetchUser(1);
// the user navigated away
request.cancel('User navigated away');The code that cancels does not need to know there is a request behind the promise. The promise handles its own cleanup.
The callback can also be an async function. An error it throws rejects the promise.
import { CancelablePromise } from 'easy-cancelable-promise';
export const load = (url: string) =>
new CancelablePromise<unknown>(async (resolve, _reject, { onCancel }) => {
const controller = new AbortController();
onCancel(() => controller.abort());
// if fetch or json throws, the promise is rejected with that error
const response = await fetch(url, { signal: controller.signal });
resolve(await response.json());
});Report progress
The utilities also include reportProgress. Whoever holds the promise listens with onProgress.
import { CancelablePromise } from 'easy-cancelable-promise';
export const upload = (chunks: string[]) =>
new CancelablePromise<number>((resolve, _reject, { onCancel, reportProgress }) => {
let sent = 0;
let timer: ReturnType<typeof setTimeout>;
onCancel(() => clearTimeout(timer));
const next = () => {
sent += 1;
reportProgress((sent / chunks.length) * 100, { sent });
if (sent === chunks.length) return resolve(sent);
timer = setTimeout(next, 10);
};
timer = setTimeout(next, 10);
});
export const example = () =>
upload(['a', 'b', 'c', 'd']).onProgress((percentage) => {
console.log(`${percentage}%`); // 25%, 50%, 75%, 100%
});Where to go next
- CancelablePromise: the constructor, the status and what happens in a chain.
- cancel and onCancel: reasons, listeners and cleanup.
- Examples: three applications running the real library, with their source.