Skip to content
Buy me a coffee

Reasons & errors

Every aborted signal has a reason. APIs that take a signal reject with that reason, unchanged. So the reason decides what your catch sees.

How it aborted signal.reason
controller.abort() DOMException, name AbortError
controller.abort(myReason) myReason, as is (an Error, a string, anything)
abortAfter / timeout() deadline DOMException, name TimeoutError
A linked controller whose parent aborted The parent’s reason, the same object
AbortSignal.timeout DOMException, name TimeoutError

Reasons are never stringified or rewrapped. throwIfAborted() (native) throws the reason too.

What an API rejects with is up to the API:

API Rejects with
fetch, throwIfAborted(), most libraries signal.reason itself
Node core: fs/promises, timers/promises, stream, events.once, child_process Node’s own AbortError (code: 'ABORT_ERR'), with signal.reason as its cause

Classify with isAbortError and isTimeoutError

Section titled “Classify with isAbortError and isTimeoutError”
import {
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
,
function isAbortError(error: unknown): boolean

True for every way an aborted operation fails: this library's

AbortError

, and any error named AbortError or TimeoutError (the DOMExceptions that fetch, AbortSignal.timeout, abortAfter and throwIfAborted produce).

@example

try {
await fetch(url, { signal: controller.signal });
} catch (error) {
if (isAbortError(error)) return; // cancelled or timed out
throw error;
}

isAbortError
,
function isTimeoutError(error: unknown): boolean

True if error is a timeout: a TimeoutError from abortAfter, timeout() or AbortSignal.timeout, also when wrapped: Node core APIs (fs/promises, timers/promises, streams, events.once) reject with an AbortError whose cause is the signal's reason.

isTimeoutError
} from '@ylcnfrht/enhanced-abort-controller';
using
using controller: EnhancedAbortController
controller
= new
new EnhancedAbortController(): 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.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
(3000);
try {
await
function fetch(input: string | URL | Request, init?: RequestInit): Promise<Response> (+1 overload)
fetch
(
const url: string
url
, {
RequestInit.signal?: AbortSignal | null | undefined

An AbortSignal to set request's signal.

signal
:
using controller: EnhancedAbortController
controller
.
EnhancedAbortController.signal: EnhancedSignal

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

signal
});
} catch (
var error: unknown
error
) {
if (!
function isAbortError(error: unknown): boolean

True for every way an aborted operation fails: this library's

AbortError

, and any error named AbortError or TimeoutError (the DOMExceptions that fetch, AbortSignal.timeout, abortAfter and throwIfAborted produce).

@example

try {
await fetch(url, { signal: controller.signal });
} catch (error) {
if (isAbortError(error)) return; // cancelled or timed out
throw error;
}

isAbortError
(
var error: unknown
error
)) throw
var error: unknown
error
; // a real failure: DNS, 500 handling, a bug
if (
function isTimeoutError(error: unknown): boolean

True if error is a timeout: a TimeoutError from abortAfter, timeout() or AbortSignal.timeout, also when wrapped: Node core APIs (fs/promises, timers/promises, streams, events.once) reject with an AbortError whose cause is the signal's reason.

isTimeoutError
(
var error: unknown
error
))
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@see ― source

console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@since ― v0.1.100

log
('took too long');
else
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@see ― source

console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@since ― v0.1.100

log
('cancelled');
}
  • isAbortError(e) is true for the library’s AbortError and for any error whose name is AbortError or TimeoutError: the DOMExceptions from fetch, AbortSignal.timeout, abortAfter and throwIfAborted(), and Node’s own AbortError from fs, timers/promises and streams.
  • isTimeoutError(e) is true for errors named TimeoutError, also when they are the cause of another error, as with Node core APIs, which wrap the reason.
using
using controller: EnhancedAbortController
controller
= new
new EnhancedAbortController(): 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.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
(100);
try {
await
sleep<undefined>(delay?: number, value?: undefined, options?: TimerOptions): Promise<undefined>

import {
setTimeout,
} from 'node:timers/promises';
const res = await setTimeout(100, 'result');
console.log(res); // Prints 'result'

@since ― v15.0.0

@param ― delay The number of milliseconds to wait before fulfilling the promise. Default: 1.

@param ― value A value with which the promise is fulfilled.

sleep
(1000,
var undefined
undefined
, {
EventEmitter<T extends EventMap<T> = DefaultEventMap>.Abortable.signal?: AbortSignal | undefined

When provided the corresponding AbortController can be used to cancel an asynchronous action.

signal
:
using controller: EnhancedAbortController
controller
.
EnhancedAbortController.signal: EnhancedSignal

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

signal
});
} catch (
var error: unknown
error
) {
if (!
function isAbortError(error: unknown): boolean

True for every way an aborted operation fails: this library's

AbortError

, and any error named AbortError or TimeoutError (the DOMExceptions that fetch, AbortSignal.timeout, abortAfter and throwIfAborted produce).

@example

try {
await fetch(url, { signal: controller.signal });
} catch (error) {
if (isAbortError(error)) return; // cancelled or timed out
throw error;
}

isAbortError
(
var error: unknown
error
)) throw
var error: unknown
error
; // Node's AbortError: true
function isTimeoutError(error: unknown): boolean

True if error is a timeout: a TimeoutError from abortAfter, timeout() or AbortSignal.timeout, also when wrapped: Node core APIs (fs/promises, timers/promises, streams, events.once) reject with an AbortError whose cause is the signal's reason.

isTimeoutError
(
var error: unknown
error
); // true: the TimeoutError is error.cause, and cause is followed
}
async function
function run(controller: EnhancedAbortController): Promise<void>
run
(
controller: EnhancedAbortController
controller
:
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
) {
try {
await
function work(signal: AbortSignal): Promise<void>
work
(
controller: EnhancedAbortController
controller
.
EnhancedAbortController.signal: EnhancedSignal

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

signal
);
} catch (
function (local var) error: unknown
error
) {
if (
controller: EnhancedAbortController
controller
.
EnhancedAbortController.signal: EnhancedSignal

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

signal
.
AbortSignal.aborted: boolean

The aborted read-only property returns a value that indicates whether the asynchronous operations the signal is communicating with are aborted (true) or not (false).

MDN Reference

aborted
) return; // whatever the reason was, we stopped it on purpose
throw
function (local var) error: unknown
error
;
}
}

Why not instanceof? DOMException, Node’s internal AbortError and this library’s AbortError are different classes. Checking the name covers all of them.

AbortError is a plain Error subclass with name = 'AbortError', for code that wants to throw or abort with its own abort error. Platform APIs don’t throw it; they throw the signal’s reason.

const
const controller: EnhancedAbortController
controller
= new
new EnhancedAbortController(): 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
();
const controller: EnhancedAbortController
controller
.
AbortController.abort(reason?: any): void

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

MDN Reference

abort
(new
new AbortError(message?: string, options?: {
cause?: unknown;
}): AbortError

An error for aborted work, for code that wants to throw its own. Platform APIs (fetch, timers, signal.throwIfAborted()) reject with the signal's reason instead, usually a DOMException named AbortError or TimeoutError; use

isAbortError

to recognise all of them.

AbortError
('Client disconnected', {
cause?: unknown
cause
:
const socketError: Error
socketError
}));
function isAbortError(error: unknown): boolean

True for every way an aborted operation fails: this library's

AbortError

, and any error named AbortError or TimeoutError (the DOMExceptions that fetch, AbortSignal.timeout, abortAfter and throwIfAborted produce).

@example

try {
await fetch(url, { signal: controller.signal });
} catch (error) {
if (isAbortError(error)) return; // cancelled or timed out
throw error;
}

isAbortError
(
const controller: EnhancedAbortController
controller
.
EnhancedAbortController.reason: unknown

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

reason
); // true
new
new AbortError(message?: string, options?: {
cause?: unknown;
}): AbortError

An error for aborted work, for code that wants to throw its own. Platform APIs (fetch, timers, signal.throwIfAborted()) reject with the signal's reason instead, usually a DOMException named AbortError or TimeoutError; use

isAbortError

to recognise all of them.

AbortError
().
Error.message: string
message
; // 'The operation was aborted.'

Honour the signal the same way the platform does: check it up front, reject with signal.reason on abort, and remove your listener when done.

function
function delay(ms: number, signal?: AbortSignal): Promise<void>
delay
(
ms: number
ms
: number,
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
):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<void> {
return new
var Promise: PromiseConstructor
new <void>(executor: (resolve: (value: void | PromiseLike<void>) => void, reject: (reason?: any) => void) => void) => Promise<void>

Creates a new Promise.

@param ― executor A callback used to initialize the promise. This callback is passed two arguments: a resolve callback used to resolve the promise with a value or the result of another promise, and a reject callback used to reject the promise with a provided reason or error.

Promise
((
resolve: (value: void | PromiseLike<void>) => void
resolve
,
reject: (reason?: any) => void
reject
) => {
signal: AbortSignal | undefined
signal
?.
AbortSignal.throwIfAborted(): void

The throwIfAborted() method throws the signal's abort AbortSignal.reason if the signal has been aborted; otherwise it does nothing.

MDN Reference

throwIfAborted
();
const
const timer: NodeJS.Timeout
timer
=
function setTimeout<[]>(callback: () => void, delay?: number): NodeJS.Timeout (+2 overloads)

Schedules execution of a one-time callback after delay milliseconds.

The callback will likely not be invoked in precisely delay milliseconds. Node.js makes no guarantees about the exact timing of when callbacks will fire, nor of their ordering. The callback will be called as close as possible to the time specified.

When delay is larger than 2147483647 or less than 1 or NaN, the delay will be set to 1. Non-integer delays are truncated to an integer.

If callback is not a function, a TypeError will be thrown.

This method has a custom variant for promises that is available using timersPromises.setTimeout().

@since ― v0.0.1

@param ― callback The function to call when the timer elapses.

@param ― delay The number of milliseconds to wait before calling the callback. Default: 1.

@param ― args Optional arguments to pass when the callback is called.

@returns ― for use with clearTimeout()

setTimeout
(() => {
const registration: AbortRegistration | undefined
registration
?.
AbortRegistration.unregister(): void

Removes the callback. Safe to call more than once.

unregister
();
resolve: (value: void | PromiseLike<void>) => void
resolve
();
},
ms: number
ms
);
const
const registration: AbortRegistration | undefined
registration
=
signal: AbortSignal | undefined
signal
&&
function register(signal: AbortSignal, callback: (reason: unknown) => void): AbortRegistration

Runs callback with the signal's reason when signal aborts, like .NET's CancellationToken.Register. Works with any AbortSignal (from fetch, a framework, AbortSignal.timeout, ...).

  • If the signal has already aborted, the callback runs synchronously, now, and its exceptions propagate to the caller of register().
  • Otherwise it runs once, as an abort event listener: an exception it throws is reported like any event-listener exception (in Node, an uncaught exception), so handle errors inside the callback.
  • Call unregister() (or using) on the result when the guarded work finishes.

register
(
signal: AbortSignal
signal
, (
reason: unknown
reason
) => {
function clearTimeout(timeout: NodeJS.Timeout | string | number | undefined): void (+1 overload)

Cancels a Timeout object created by setTimeout().

@since ― v0.0.1

@param ― timeout A Timeout object as returned by setTimeout() or the primitive of the Timeout object as a string or a number.

clearTimeout
(
const timer: NodeJS.Timeout
timer
);
reject: (reason?: any) => void
reject
(
reason: unknown
reason
);
});
});
}