CancellableToken
CancellableToken is passed to the async function inside cancellable. It provides utilities for cooperative cancellation: wrapping promises, sleeping, running intervals, animation frames, checking cancellation status, and registering cleanup callbacks.
Internal interface
CancellableTokenis what the task receives. The caller holds aCancellableHandle.
Import
Section titled “Import”Root package:
import { CancellableToken } from "@vgerbot/async";Module subpath:
import { CancellableToken } from "@vgerbot/async/cancellable";Leaf subpath:
import { CancellableToken } from "@vgerbot/async/cancellable/CancellableToken";Quick example
Section titled “Quick example”import { cancellable } from "@vgerbot/async";
const handle = cancellable(async (token) => { // Cancellable sleep — rejects with CancelError if cancelled await token.sleep(5000);
// Wrap a promise so it rejects on cancellation const response = await token.wrap(fetch("/api/data"));
// Explicit cancellation check token.throwIfCancelled();
return response.json();});
handle.cancel("User navigated away");class CancellableToken { readonly signal: AbortSignal; readonly name: string | undefined;
// Retry state get retryAttempt: number;
// Cancellation state isCancelled(): boolean; throwIfCancelled(): void; onCancel(callback: (error: CancelError) => void): () => void;
// Promise wrapping wrap<T>(p: CancellableHandle<T> | Promise<T>): Promise<T>;
// Time utilities sleep(ms: number): CancellableHandle<void>; frame(): CancellableHandle<void>; delay(schedule: (done: () => void) => () => void): CancellableHandle<void>; interval(fn: () => void | Promise<void>, interval: number): CancellableHandle<void>;}Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
signal |
AbortSignal |
The underlying AbortSignal that drives the token’s cancellation. When the parent CancellableHandle is cancelled (or a linked signal/token aborts), this signal aborts. Pass it directly to native APIs that accept AbortSignal, e.g. fetch(url, { signal: token.signal }). |
name |
string | undefined |
The name assigned to the task (from CancellableOptions.name). Used in cancellation messages and error labels. |
retryAttempt |
number |
The current retry attempt number (0-indexed). 0 for the initial attempt, 1 for the first retry, and so on. Useful for tracking retry progress when retry options are configured. |
Methods
Section titled “Methods”isCancelled()
Section titled “isCancelled()”Returns true if the token has been cancelled (i.e. the underlying AbortSignal has been aborted).
async (token) => { if (token.isCancelled()) { return fallbackValue; } // continue work}throwIfCancelled()
Section titled “throwIfCancelled()”Throws a CancelError if the token has been cancelled. Use this to check for cancellation at strategic points in long-running tasks.
async (token) => { for (const item of largeArray) { token.throwIfCancelled(); await processItem(item); }}onCancel(callback)
Section titled “onCancel(callback)”Registers a callback invoked when the token is cancelled. If the token is already cancelled, the callback is invoked immediately. Returns a cleanup function to remove the listener.
The callback receives the resolved CancelError.
async (token) => { const connection = await openConnection(); const unsubscribe = token.onCancel((error) => { console.log("Task cancelled:", error.message); connection.close(); });
// Later, to remove the listener: // unsubscribe();
return doWork(connection);}wrap(p)
Section titled “wrap(p)”Wraps a Promise or CancellableHandle so that it rejects with a CancelError if the token is cancelled.
When wrapping a CancellableHandle, cancellation is forwarded to the nested handle — calling cancel() on the parent will also cancel the wrapped handle.
async (token) => { // Wrap a plain promise const response = await token.wrap(fetch("/api/data")); return response.json();}async (token) => { // Wrap a nested CancellableHandle — cancellation cascades const handle = cancellable(async (innerToken) => { await innerToken.sleep(1000); return "done"; });
const result = await token.wrap(handle); return result;}
wrapvssignalUsetoken.wrap(promise)for promises that don’t natively supportAbortSignal. Usetoken.signaldirectly with APIs that accept it (e.g.,fetch(url, { signal: token.signal })).
sleep(ms)
Section titled “sleep(ms)”Creates a cancellable sleep/delay that resolves after ms milliseconds. Returns a CancellableHandle<void> — if the token is cancelled during the sleep, the handle rejects with a CancelError. The handle can also be cancelled independently of the parent token.
async (token) => { await token.sleep(1000); console.log("1 second elapsed");}frame()
Section titled “frame()”Creates a cancellable animation frame. Uses requestAnimationFrame if available, otherwise falls back to setTimeout (~16ms). Returns a CancellableHandle<void> that resolves on the next animation frame, or rejects with a CancelError if cancelled before the frame callback.
async (token) => { await token.frame(); // Next animation frame reached}delay(schedule)
Section titled “delay(schedule)”Creates a cancellable delay using a custom scheduling function. The scheduling function receives a callback to invoke when the delay completes, and must return a cleanup function to cancel the scheduled operation.
The delay stops when:
- The parent token is cancelled
- The returned handle is cancelled
async (token) => { // Custom delay using setTimeout await token.delay((done) => { const timer = setTimeout(done, 1000); return () => clearTimeout(timer); });}async (token) => { // Cancel independently const handle = token.delay((done) => { const timer = setTimeout(done, 5000); return () => clearTimeout(timer); }); handle.cancel();}interval(fn, interval)
Section titled “interval(fn, interval)”Executes fn repeatedly, waiting interval ms between each execution (after the previous one completes). The function can be synchronous or asynchronous. Returns a CancellableHandle<void> that can be used to cancel the interval independently of the parent token.
The interval stops when:
- The parent token is cancelled
- The returned handle is cancelled
fnthrows an error (non-CancelErrorerrors reject with the original error)
When the interval stops due to cancellation (parent token or handle), the handle resolves with undefined — it does not reject. This allows await handle to complete silently. You can still check handle.isCancelled() to determine if it was cancelled.
When fn throws a non-CancelError error, the handle rejects with that original error.
async (token) => { // Poll an API every 5 seconds const handle = token.interval(async () => { const data = await fetchData(); processData(data); }, 5000);
// Cancel the interval independently handle.cancel();}Cancellation patterns
Section titled “Cancellation patterns”Polling with cancellation
Section titled “Polling with cancellation”const handle = cancellable(async (token) => { while (true) { token.throwIfCancelled(); const status = await token.wrap(fetch("/api/status")); if (status.ok) return status.json(); await token.sleep(1000); }});Wrapping native fetch
Section titled “Wrapping native fetch”const handle = cancellable(async (token) => { // Option 1: Use signal directly (preferred for fetch) const res = await fetch("/api/data", { signal: token.signal });
// Option 2: Use wrap (for APIs that don't support AbortSignal) const res2 = await token.wrap(fetch("/api/data"));
return res.json();});Cleanup on cancellation
Section titled “Cleanup on cancellation”const handle = cancellable(async (token) => { const stream = await openStream(); token.onCancel(() => stream.close());
const data = await token.wrap(stream.read()); return data;});Nested cancellable tasks
Section titled “Nested cancellable tasks”const handle = cancellable(async (token) => { // Cancellation cascades to the nested task const result = await token.wrap( cancellable(async (innerToken) => { await innerToken.sleep(1000); return "inner done"; }) ); return result;});TypeScript tips
Section titled “TypeScript tips”CancellableToken is not generic — it doesn’t carry a type parameter. The token is purely for cancellation control.
const task: AsyncTask<MyResult> = async (token: CancellableToken) => { // token has no type parameter return myResult;};Related APIs
Section titled “Related APIs”cancellable— creates a task with aCancellableToken.CancellableHandle— the handle returned to the caller.CancelError— the error type thrown on cancellation.delay— a standalone cancellable delay.