Asynchronous cancellation
Follow cancellation from .NET tokens through Promise and web APIs, separating a stop request, actual termination, and stale results.
· Last updated: 2026-11-04 · 6 min read
Overview
Cancellation lets a caller tell an operation that its work is no longer needed and coordinate its lifetime so that the work ends or its result is no longer used. It matters when an image keeps downloading after a screen closes, or when an old search finishes late and replaces the result of a newer search.
Several layers can be involved: ending a wait for a result, stopping computation or communication, and declining to apply a completed result. These are different operations. The API contract determines which layer a cancellation request controls.
Once work starts, the caller may not directly control its execution. A network request reaches a remote server, file access involves operating-system I/O, and a computation can occupy the event loop. Many APIs therefore use cooperative cancellation: they pass a signal that the operation observes and responds to through cleanup and termination.
Cancellation is also where lifetime management meets concurrency control. A new search starting and an old search completing can change the screen differently depending on their order. Understanding the design requires examining both attempts to stop work and authority to update current state.
Chronology
May 2009: In “.NET 4 Cancellation Framework” on the Microsoft .NET Blog, Mike Liddell introduced the .NET 4 design, separating
CancellationTokenSourcefor requesting cancellation fromCancellationTokenpassed to the operation. His contemporary design account describes adopting this approach throughout Parallel Extensions.1April 2010:
CancellationTokenSourceandCancellationTokenbecame part of .NET Framework 4.23March 2015: Issue #27, “Aborting a fetch,” opened in the WHATWG fetch repository to discuss a cancellation interface.45
June 2015: ECMAScript 2015, the sixth edition of ECMA-262, included Promise in the JavaScript standard.6
2017: Browsers began shipping fetch cancellation. Jake Archibald’s “Abortable fetch” traces the discussion from 2015; its correction identifies Edge 16 as the first browser to ship support.4
Forced interruption and cooperative cancellation
Introducing the .NET 4 cancellation framework in 2009, Mike Liddell compared two approaches. Forcibly interrupting execution can stop code before it has restored shared data or released resources. Cooperative cancellation lets the operation observe a request and perform its own cleanup.1
The account also describes the difficulty of APIs with separate cancellation methods or flags on each operation object. Combining operations under one request, or distinguishing a particular execution of a reusable object, becomes awkward. The object doing work and the lifetime of one execution need separate identities.
The .NET design separates CancellationTokenSource, which requests cancellation, from the CancellationToken passed to work. An operation can check its token or register a callback; several operations can observe one request.1 The split makes authority to request cancellation distinct from responsibility to respond. Current .NET documentation likewise describes a cooperative process of requesting, observing, and performing operation-specific cleanup. The request and actual termination need not happen at the same instant.7
Waiting for a result and stopping execution
ECMAScript 2015 included Promise in the JavaScript standard. A Promise represents a result and reactions to its eventual fulfillment or rejection. Its state changes from pending to fulfilled or rejected.6 Several consumers can await one result, and a Promise does not determine whether its producer performs communication or computation. An interface for observing a result therefore has a different responsibility from an interface controlling the producer's lifetime.
Racing a request against a timer can limit how long a consumer waits. Ending the remaining request's resource use or remote processing requires cancellation facilities supported by that work.
Cancellation signals and authority to apply results
The DOM's AbortController and AbortSignal also separate requesters from observers. A controller requests cancellation; its signal records the aborted state and reason. Each accepting API defines how it responds. The roles of these two types can be compared with the requester/observer split in .NET.8
The current DOM standard defines a common signal for aborting activities, and the Fetch standard connects it to requests and response bodies. Body consumption can continue after a response object arrives, so receiving a response and finishing its body are distinct events.89
Cooperative cancellation proceeds through request, observation, cleanup, and completion. A long synchronous computation needs observation points and opportunities for other code to execute. Passing a signal object does not insert those points into the computation.
Remote writes have a separate lifetime. If the server has committed by the time communication stops, an application contract such as an operation-status query or a separate cancellation command determines what follows.
Search A may finish after a later search B. Applying results in completion order can leave the older A on screen. React's Effect documentation demonstrates ignoring stale responses during cleanup to address this race.10
The design combines requests to reduce obsolete work with authority to update current state. A request identity or generation can govern success, error display, and loading-state cleanup alike. A parent feature's lifetime also determines which child operations must end with it.
Reading cancellation state
The following code isolates the roles of controller and signal. It performs no communication or asynchronous computation; it reads the signal before and after a cancellation request.
function inspect(signal: AbortSignal): string {
return signal.aborted ? 'cancelled' : 'ready';
}
const controller = new AbortController();
console.log(inspect(controller.signal));
controller.abort();
console.log(inspect(controller.signal));
The output is ready, then cancelled. Calling abort() changes the signal's state and inspect reads it. Stopping real work additionally requires observation points inside that work or an API that supports cancellation.
Footnotes
- Mike Liddell, “.NET 4 Cancellation Framework,” Microsoft .NET Blog, May 22, 2009. A contemporary account of the design problems and token/source split. Article. ↩
- Microsoft, “Cancellation” (.NET Framework 4 archived documentation). Describes the unified cooperative cancellation model introduced in .NET Framework 4 around CancellationTokenSource and CancellationToken. Official documentation. ↩
- Microsoft, “Microsoft .NET Framework - Microsoft Lifecycle.” Lists April 12, 2010 as the start date for .NET Framework 4.0. Official documentation. ↩
- Jake Archibald, “Abortable fetch,” 2017, including “The history.” Author's article. ↩
- WHATWG, whatwg/fetch issue #27, “Aborting a fetch,” opened March 26, 2015. The issue body also links to earlier discussion. Issue. ↩
- ECMA-262, sixth edition, 2015, §25.4 “Promise Objects.” Historical standard. ↩
- Microsoft, “Cancellation in Managed Threads.” The current .NET contract for requesting, observing, and cleaning up cancellation. Official documentation. ↩
- WHATWG DOM, §3 “Aborting ongoing activities.” Standard. ↩
- WHATWG Fetch,
fetch()and abort algorithms. Standard. ↩ - React, “Fetching data with Effects.” Official documentation. ↩