Skip to content
Buy me a coffee

Linked controllers

EnhancedAbortController.createLinkedController(...signals) is .NET’s CreateLinkedTokenSource. The new controller aborts as soon as any of the signals aborts, with that signal’s reason. linkSignals is an alias.

{
// Stops when the request aborts, or after 2 s, whichever comes first.
using
using step: EnhancedAbortController
step
=
class EnhancedAbortController

An AbortController with .NET CancellationTokenSource features. It is an AbortController, and controller.signal is a real AbortSignal: pass it to fetch, AbortSignal.any, streams, timers and any other API that takes a signal.

@example

const controller = new EnhancedAbortController().abortAfter(TimeSpan.fromSeconds(5));
const response = await fetch(url, { signal: controller.signal });

EnhancedAbortController
.
EnhancedAbortController.createLinkedController<EnhancedAbortController>(this: new () => EnhancedAbortController, ...signals: Array<AbortSignal | null | undefined>): EnhancedAbortController

A controller that aborts as soon as any of signals aborts, with that signal's reason, like .NET's CreateLinkedTokenSource. Aborting the linked controller does not affect the parents. Its listeners on the parents are removed when it aborts or is disposed, so short-lived links on a long-lived parent don't leak. undefined/null entries (optional signals) and EnhancedAbortSignal.none are skipped.

createLinkedController
(
const request: {
signal: AbortSignal;
}
request
.
signal: AbortSignal
signal
).
EnhancedAbortController.abortAfter(delay: Delay): EnhancedAbortController

Aborts after delay, like .NET's CancelAfter: calling it again replaces the previous schedule, and Infinity / -1 / TimeSpan.infinite cancel it. The reason is a DOMException named TimeoutError, as with AbortSignal.timeout. The timer never keeps the process alive, and delays longer than 24.8 days work. Does nothing once aborted. Returns the controller, for chaining.

@throws ― RangeError for NaN, negative (other than -1) or non-numeric delays.

@throws ― Error if the controller was disposed.

abortAfter
(2000);
await
function queryOrders(signal: AbortSignal): Promise<void>
queryOrders
(
using step: EnhancedAbortController
step
.
EnhancedAbortController.signal: EnhancedSignal

The controller's signal: a real AbortSignal, always the same object.

signal
);
}

A request signal with three steps, each linked to it with its own deadline. Let a step time out, abort the parent, dispose a step, or create 1000 short-lived children, and watch the listener count on the parent.

  • Reason forwarding. When a parent aborts, the child aborts with the parent’s reason, the same object. A child that times out on its own has a TimeoutError.
  • One way. Aborting or disposing the child never touches the parents.
  • Already aborted parent. Linking to a signal that has already aborted gives a child that is aborted at once, with that reason.
  • Clean-up. The child puts one listener on each parent. All of them are removed when the child aborts (for any reason, including one parent aborting) or is disposed. Short-lived children on a long-lived parent leave nothing behind.
  • Optional signals. undefined and null entries are skipped, and so is EnhancedAbortSignal.none, so you can pass an optional caller signal straight in. With no signals left, the child is just a fresh controller.
  • Subclasses. Called on a subclass, createLinkedController and timeout return an instance of that subclass.
const
const parent: AbortController
parent
= new
var AbortController: new () => AbortController

The AbortController interface represents a controller object that allows you to abort one or more Web requests as and when desired.

MDN Reference

AbortController
();
const
const child: EnhancedAbortController
child
=
class EnhancedAbortController

An AbortController with .NET CancellationTokenSource features. It is an AbortController, and controller.signal is a real AbortSignal: pass it to fetch, AbortSignal.any, streams, timers and any other API that takes a signal.

@example

const controller = new EnhancedAbortController().abortAfter(TimeSpan.fromSeconds(5));
const response = await fetch(url, { signal: controller.signal });

EnhancedAbortController
.
EnhancedAbortController.createLinkedController<EnhancedAbortController>(this: new () => EnhancedAbortController, ...signals: Array<AbortSignal | null | undefined>): EnhancedAbortController

A controller that aborts as soon as any of signals aborts, with that signal's reason, like .NET's CreateLinkedTokenSource. Aborting the linked controller does not affect the parents. Its listeners on the parents are removed when it aborts or is disposed, so short-lived links on a long-lived parent don't leak. undefined/null entries (optional signals) and EnhancedAbortSignal.none are skipped.

createLinkedController
(
const parent: AbortController
parent
.
AbortController.signal: AbortSignal

The signal read-only property of the AbortController interface returns an AbortSignal object instance, which can be used to communicate with/abort an asynchronous operation as desired.

MDN Reference

signal
);
const parent: AbortController
parent
.
AbortController.abort(reason?: any): void

The abort() method of the AbortController interface aborts an asynchronous operation before it has completed.

MDN Reference

abort
(new
var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error
('client disconnected'));
const child: EnhancedAbortController
child
.
EnhancedAbortController.reason: unknown

Same as signal.reason: why it aborted, undefined until then.

reason
===
const parent: AbortController
parent
.
AbortController.signal: AbortSignal

The signal read-only property of the AbortController interface returns an AbortSignal object instance, which can be used to communicate with/abort an asynchronous operation as desired.

MDN Reference

signal
.
AbortSignal.reason: any

The reason read-only property returns a JavaScript value that indicates the abort reason.

MDN Reference

reason
; // true
async function
function load(id: string, signal?: AbortSignal): Promise<Response>
load
(
id: string
id
: string,
signal: AbortSignal | undefined
signal
?:
interface AbortSignal

The AbortSignal interface represents a signal object that allows you to communicate with an asynchronous operation (such as a fetch request) and abort it if required via an AbortController object.

MDN Reference

AbortSignal
) {
using
using local: EnhancedAbortController
local
=
class EnhancedAbortController

An AbortController with .NET CancellationTokenSource features. It is an AbortController, and controller.signal is a real AbortSignal: pass it to fetch, AbortSignal.any, streams, timers and any other API that takes a signal.

@example

const controller = new EnhancedAbortController().abortAfter(TimeSpan.fromSeconds(5));
const response = await fetch(url, { signal: controller.signal });

EnhancedAbortController
.
EnhancedAbortController.createLinkedController<EnhancedAbortController>(this: new () => EnhancedAbortController, ...signals: Array<AbortSignal | null | undefined>): EnhancedAbortController

A controller that aborts as soon as any of signals aborts, with that signal's reason, like .NET's CreateLinkedTokenSource. Aborting the linked controller does not affect the parents. Its listeners on the parents are removed when it aborts or is disposed, so short-lived links on a long-lived parent don't leak. undefined/null entries (optional signals) and EnhancedAbortSignal.none are skipped.

createLinkedController
(
signal: AbortSignal | undefined
signal
).
EnhancedAbortController.abortAfter(delay: Delay): EnhancedAbortController

Aborts after delay, like .NET's CancelAfter: calling it again replaces the previous schedule, and Infinity / -1 / TimeSpan.infinite cancel it. The reason is a DOMException named TimeoutError, as with AbortSignal.timeout. The timer never keeps the process alive, and delays longer than 24.8 days work. Does nothing once aborted. Returns the controller, for chaining.

@throws ― RangeError for NaN, negative (other than -1) or non-numeric delays.

@throws ― Error if the controller was disposed.

abortAfter
(5000); // signal may be undefined
return await
function fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> (+1 overload)
fetch
(`/items/${
id: string
id
}`, {
RequestInit.signal?: AbortSignal | null | undefined

An AbortSignal to set request's signal.

signal
:
using local: EnhancedAbortController
local
.
EnhancedAbortController.signal: EnhancedSignal

The controller's signal: a real AbortSignal, always the same object.

signal
}); // await: dispose only after the fetch settles
}

createLinkedController or AbortSignal.any?

Section titled “createLinkedController or AbortSignal.any?”

Both forward the first reason. Pick by what you need:

createLinkedController AbortSignal.any / EnhancedAbortSignal.any
Result A controller: you can abort() it and give it abortAfter A signal only
Parent listeners released When it aborts or is disposed: deterministic When the combined signal is garbage-collected
Optional entries Skipped EnhancedAbortSignal.any skips them; native AbortSignal.any throws

EnhancedAbortSignal.any delegates to native AbortSignal.any, so use it when you just need one signal from several.