Skip to content
easy-cancelable-promise

Abort signals

Remove cancel and progress listeners with an AbortSignal, and manage many subscriptions with CancelableAbortController.

GuideTypeScriptTry an example

Signals remove listeners

onCancel and onProgress accept an options object with a signal. When the signal is aborted, the listener is removed. The promise itself is not affected.

with-promise.ts
import { CancelableAbortController, CancelablePromise } from 'easy-cancelable-promise';

export function mounted() {
  const calls: string[] = [];
  const task = new CancelablePromise<void>(() => {});

  // one controller for everything a screen subscribes to
  const screen = new CancelableAbortController();

  task.onCancel(() => calls.push('update the screen'), { signal: screen.signal });
  task.onProgress(() => calls.push('move the progress bar'), { signal: screen.signal });

  // a native AbortSignal works too
  const native = new AbortController();

  task.onCancel(() => calls.push('native listener'), { signal: native.signal });

  // the screen is gone: remove its listeners, the task keeps running
  screen.abort();
  native.abort();

  task.reportProgress(50);
  task.cancel();

  return [calls, task.status]; // [[], 'canceled']
}

This is the pattern for a screen or a component: one controller for everything it subscribes to, aborted when it goes away. The task keeps running for whoever else needs it.

CancelableAbortController

CancelableAbortController extends the native AbortController. Its signal has a subscribe method that returns the function to remove the listener, and the controller keeps track of every subscription.

controller.ts
import { CancelableAbortController, isCancelableAbortSignal } from 'easy-cancelable-promise';

export function controller() {
  const calls: string[] = [];
  const abortController = new CancelableAbortController();

  // subscribe listens to the abort event and returns the function that removes the listener
  const unsubscribe = abortController.signal.subscribe(() => calls.push('first'));

  abortController.signal.subscribe(() => calls.push('second'));

  const active = abortController.subscriptions.length; // 2

  unsubscribe();

  // abort calls the listeners that are left and then removes them all
  abortController.abort();

  return {
    calls, // ['second']
    active,
    aborted: abortController.signal.aborted, // true
    isCancelable: isCancelableAbortSignal(abortController.signal), // true
    isNativeCancelable: isCancelableAbortSignal(new AbortController().signal), // false
  };
}
MemberWhat it does
signal.subscribe(listener, options?)Listens to the abort event. Returns the function to remove it
signal.subscribe(type, listener, options?)Listens to another event type of the signal
subscriptionsThe functions that remove the active subscriptions
abort()Aborts the signal, then removes every subscription
dispose()Removes every subscription without aborting the signal

isCancelableAbortSignal(signal) tells a signal of this controller from a native one.

dispose

After abort or dispose the controller accepts no more subscriptions.

dispose.ts
import { CancelableAbortController } from 'easy-cancelable-promise';

export function dispose() {
  const calls: string[] = [];
  const controller = new CancelableAbortController();

  controller.signal.subscribe(() => calls.push('never called'));

  // dispose removes the listeners without aborting the signal
  controller.dispose();

  let error = '';

  try {
    controller.signal.subscribe(() => {});
  } catch (reason) {
    error = (reason as Error).message;
  }

  return [calls, controller.signal.aborted, error];
  // [[], false, 'AbortController was already aborted or disposed.']
}

Next

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

Promises you can actually control.