Skip to content

Commit

Permalink
lib: add reason to AbortSignal
Browse files Browse the repository at this point in the history
A new reason property was recently added to the AbortSignal spec.

```js
const ac = new AbortController();
ac.abort(new Error('boom!'));
console.loc(ac.signal.reason);  // Error('boom!');
```

Signed-off-by: James M Snell <jasnell@gmail.com>

PR-URL: #40807
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
Reviewed-By: Robert Nagy <ronagy@icloud.com>
  • Loading branch information
jasnell authored and danielleadams committed Feb 1, 2022
1 parent 28a216a commit e08df49
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 10 deletions.
40 changes: 36 additions & 4 deletions doc/api/globals.md
Expand Up @@ -44,12 +44,21 @@ ac.abort();
console.log(ac.signal.aborted); // Prints True
```

### `abortController.abort()`
### `abortController.abort([reason])`

<!-- YAML
added: v15.0.0
added:
- v15.0.0
- v14.17.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/40807
description: Added the new optional reason argument.
-->

* `reason` {any} An optional reason, retrievable on the `AbortSignal`s
`reason` property.

Triggers the abort signal, causing the `abortController.signal` to emit
the `'abort'` event.

Expand All @@ -72,12 +81,19 @@ added: v15.0.0
The `AbortSignal` is used to notify observers when the
`abortController.abort()` method is called.

#### Static method: `AbortSignal.abort()`
#### Static method: `AbortSignal.abort([reason])`

<!-- YAML
added: v15.12.0
added:
- v15.12.0
- v14.17.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/40807
description: Added the new optional reason argument.
-->

* `reason`: {any}
* Returns: {AbortSignal}

Returns a new already aborted `AbortSignal`.
Expand Down Expand Up @@ -136,6 +152,22 @@ added: v15.0.0
An optional callback function that may be set by user code to be notified
when the `abortController.abort()` function has been called.

#### `abortSignal.reason`

<!-- YAML
added: REPLACEME
-->

* Type: {any}

An optional reason specified when the `AbortSignal` was triggered.

```js
const ac = new AbortController();
ac.abort(new Error('boom!'));
console.log(ac.signal.reason); // Error('boom!');
```

## Class: `Buffer`

<!-- YAML
Expand Down
36 changes: 30 additions & 6 deletions lib/internal/abort_controller.js
Expand Up @@ -30,6 +30,7 @@ const {
} = require('internal/errors');

const kAborted = Symbol('kAborted');
const kReason = Symbol('kReason');

function customInspect(self, obj, depth, options) {
if (depth < 0)
Expand All @@ -52,19 +53,34 @@ class AbortSignal extends EventTarget {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}

/**
* @type {boolean}
*/
get aborted() {
validateAbortSignal(this);
return !!this[kAborted];
}

/**
* @type {any}
*/
get reason() {
validateAbortSignal(this);
return this[kReason];
}

[customInspectSymbol](depth, options) {
return customInspect(this, {
aborted: this.aborted
}, depth, options);
}

static abort() {
return createAbortSignal(true);
/**
* @param {any} reason
* @returns {AbortSignal}
*/
static abort(reason) {
return createAbortSignal(true, reason);
}
}

Expand All @@ -81,16 +97,18 @@ ObjectDefineProperty(AbortSignal.prototype, SymbolToStringTag, {

defineEventHandler(AbortSignal.prototype, 'abort');

function createAbortSignal(aborted = false) {
function createAbortSignal(aborted = false, reason = undefined) {
const signal = new EventTarget();
ObjectSetPrototypeOf(signal, AbortSignal.prototype);
signal[kAborted] = aborted;
signal[kReason] = reason;
return signal;
}

function abortSignal(signal) {
function abortSignal(signal, reason) {
if (signal[kAborted]) return;
signal[kAborted] = true;
signal[kReason] = reason;
const event = new Event('abort', {
[kTrustEvent]: true
});
Expand All @@ -112,14 +130,20 @@ class AbortController {
this[kSignal] = createAbortSignal();
}

/**
* @type {AbortSignal}
*/
get signal() {
validateAbortController(this);
return this[kSignal];
}

abort() {
/**
* @param {any} reason
*/
abort(reason) {
validateAbortController(this);
abortSignal(this[kSignal]);
abortSignal(this[kSignal], reason);
}

[customInspectSymbol](depth, options) {
Expand Down
13 changes: 13 additions & 0 deletions test/parallel/test-abortcontroller.js
Expand Up @@ -140,3 +140,16 @@ const { ok, strictEqual, throws } = require('assert');
strictEqual(inspect(ac, { depth: null }),
'AbortController { signal: AbortSignal { aborted: false } }');
}

{
// Test AbortSignal.reason
const ac = new AbortController();
ac.abort('reason');
strictEqual(ac.signal.reason, 'reason');
}

{
// Test AbortSignal.reason
const signal = AbortSignal.abort('reason');
strictEqual(signal.reason, 'reason');
}

0 comments on commit e08df49

Please sign in to comment.