CANCELLATION PRIMITIVES
Ron Buckton - Microsoft Corporation
Clear and consistent approach to the cancellation of
asynchronous operations
General purpose coordination primitive
Host API integration (fetch, module loading, etc.)
MOTIVATIONS
Separation of source and sink
Cancellation request can be observed synchronously, via
property
Cancellation request can be observed asynchronously, via
callback
Able to unregister callbacks
Composable for non-trivial cancellation graphs
GOALS
Source
Sink
Owned by caller
Received by callee
Can share entangled Sink with
multiple callees
Can observe cancellation state
Cannot initiate cancellation signal
on Source
Initiates cancellation signal
SOURCE/SINK
Callee A cannot interfere with
Callee B using the same Sink
Synchronous
Asynchronous
Observe cancellation request via
property
Observe cancellation request via
callback
Useful for loops and multi-phase
operations in async functions
Able to unregister/unsubscribe from
cancellation
Game Loops
Async Iteration
Best for async functions
OBSERVABILITY
Allows GC of closures
Stop listening when callback is no
longer needed
Best for non-async functions that
return Promise
Parent/Child
Root
Aggregation
Child1
Source1
Child2
Source2
Child3
Source3
COMPOSABLE
Aggregate
Promise as Token
AbortController (WHATWG)
CancellationToken (TC39)
CURRENT PROPOSALS
function asyncfn(token) {
token.then(() => { /*cancelled*/ });
}
let cancel, token = new Promise(resolve => cancel = resolve);
asyncfn(token);
cancel();
PROMISE AS TOKEN
Uses existing API
Cannot be observed synchronously
Can be observed asynchronously
Cannot be unregistered
Must use additional logic to prevent execution
Closure persists for lifetime of source
Not easily composable
PROMISE AS TOKEN
function asyncfn(signal) {
const onabort = () => { /*cancelled*/ };
signal.addEventListener('abort', onabort);
signal.removeEventListener('abort', onabort);
if (signal.aborted) { /*cancelled */ }
}
const controller = new AbortController();
asyncfn(controller.signal);
controller.abort();
ABORTCONTROLLER (WHATWG)
New API: https://github.com/whatwg/dom/pull/437
Can be observed synchronously
Can be observed asynchronously
Takes dependency on DOM events
Callbacks can be removed (via removeEventListener)
ABORTCONTROLLER (WHATWG)
function asyncfn(token) {
const reg = token.register(() => { /*cancelled*/ });
reg.unregister();
if (token.cancellationRequested) { /*cancelled*/ }
}
const source = new CancellationTokenSource();
asyncfn(source.token);
source.cancel();
CANCELLATIONTOKEN
New API: https://github.com/tc39/proposal-cancellation
Can be observed synchronously
Can be observed asynchronously
Callbacks can be removed (via unregister)
Easily composable:
async function asyncfn(token1, token2) {
const source = new CancellationTokenSource([token1, token2]);
setTimeout(() => source.cancel(), 500);
await otherfn(source.token);
}
CANCELLATIONTOKEN
CancellationTokenSource
new CancellationTokenSource([linkedTokens])
source.token
source.cancel()
source.close()
CANCELLATIONTOKEN API
CancellationToken
CancellationToken.none
CancellationToken.canceled
token.cancellationRequested
token.canBeCanceled
token.throwIfCancellationRequested()
token.register(callback)
registration.unregister()
CANCELLATIONTOKEN API
Promise
new Promise(executor, token)
promise.then(onfulfill, onreject, token)
Observable
observable.subscribe(observer, token)
Dynamic import
import(“module”, token)
POSSIBLE FUTURE INTEROP
Stage 0
Identified Champions: Ron Buckton, Brian Terlson
Strawman available
Early Spec Proposal available
https://tc39.github.io/proposal-cancellation/
Prototype available
https://github.com/tc39/proposal-cancellation
https://github.com/rbuckton/prex
Requesting Stage 1
STATUS
© Copyright 2026 Paperzz