Abort signals
Remove cancel and progress listeners with an AbortSignal, and manage many subscriptions with CancelableAbortController.
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.
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.
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
};
}| Member | What 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 |
subscriptions | The 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.
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.']
}