Skip to content
easy-cancelable-promise
LIVESame work. Two promises.

The cancelable promise
you didn't know you needed.

Promises you can actually control.

Native promises can't be canceled. Their status can't be tracked. Once started, they run to completion. Always.

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

// If you know this...
const native = new Promise((resolve, reject) => {
  // ...
});

// You know this!
const cancelable = new CancelablePromise((resolve, reject, { onCancel }) => {
  // ...
});
solution.ts
import { CancelablePromise } from 'easy-cancelable-promise';

const fetchUser = new CancelablePromise(
  async (resolve, reject, { onCancel }) => {
    const controller = new AbortController();

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

    const user = await fetch('/api/user', { signal: controller.signal }).then(
      (res) => res.json(),
    );

    resolve(user);
  },
);

// User navigated? Just cancel it.
fetchUser.cancel('User navigated away');
100% Promise compatible TypeScript ready Zero dependencies Open source · MIT
CANCELLATION

Stop waiting.
Stop the work too.

The same work runs in a native promise and in a CancelablePromise. Press Stop. With the native promise you can only stop listening: every remaining step still runs. The CancelablePromise is told to stop, and it does.

Open the full example

This runs in your browser, against the same package files that are published to npm.

Loading the live example…

EXECUTOR UTILITIES

The third parameter
gives you superpowers.

The core class that extends native Promise with cancellation and lifecycle management.

See every utility
executor-utilities.ts
import { CancelablePromise } from 'easy-cancelable-promise';

new CancelablePromise(
  (
    resolve,
    reject,
    { cancel, onCancel, reportProgress, status, isCanceled, isPending },
  ) => {
    // ✅ cancel: Cancel from inside
    cancel('Internal cancellation');

    // ✅ onCancel: Subscribe to cancellation
    const cleanup = onCancel((reason) => {
      console.log('Canceled:', reason);
    });

    // ✅ reportProgress: Report progress
    reportProgress(50); // 50% complete

    // ✅ status: Get current status
    console.log(status()); // 'pending'

    // ✅ isCanceled: Check if canceled
    if (isCanceled()) return;

    // ✅ isPending: Check if still pending
    if (isPending()) {
      // Continue work
    }
  },
);

Loading the live example…

PROGRESS

Built-in
progress tracking.

A native promise says one thing, at the end. A CancelablePromise reports while it works, and whoever holds it can listen. No extra libraries needed, and no second channel to carry around.

Report and track progress

The reports also reach the promises chained from it with then.

GROUPS

Six tasks.
One cancel.

groupAsCancelablePromise runs a list of tasks with a concurrency limit and returns one promise. Cancel it and the running tasks stop, and the ones in the queue never start. A queue written with native promises keeps going.

Group and limit concurrency

The results come back in the order of the tasks, whichever finishes first.

Loading the live example…

Simple to start.
Room for the real world.

Begin with one promise. Reach for the rest only when the application needs it.

Resolve it from anywhere.

defer() returns the promise with its resolve, reject and cancel outside the callback. Made for events, queues and tests.

Use deferred promises

Promise.all, cancelable.

all, allSettled and race return a promise you can cancel, and canceling it cancels every promise inside.

See the static helpers

Your existing promises, too.

toCancelablePromise accepts a native promise, a value or a function, and gives back a promise with a status and a cancel method.

Convert native promises

A few useful details.

No mystery around the small print.

Is it a real Promise?

Yes. CancelablePromise extends the native Promise. It works with async / await, then, catch, Promise.all and any code that expects a promise.

Does canceling always stop the work?

Canceling tells everyone to stop waiting. The work stops when the promise has an onCancel listener that stops it: clears the timer, aborts the request. How cancellation works.

What does await see when a promise is canceled?

A rejection with the reason you passed to cancel. The status of the promise is 'canceled', so a cancellation can be told from a failure, and canceling never causes an unhandled rejection.

Does it depend on a framework or a runtime?

No. It has zero dependencies and runs wherever Promise does, in browsers and in Node. Platform and versions.

CLEAN. SIMPLE.

Your promises,
your control.

The promise handles its own cleanup. Cancel from anywhere, anytime.

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

Promises you can actually control.