Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8590eb4

Browse filesBrowse files
debadree25MylesBorins
authored andcommitted
lib: add aborted() utility function
Fixes: #37220 Refs: #36607 PR-URL: #46494 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 641e177 commit 8590eb4
Copy full SHA for 8590eb4

File tree

Expand file treeCollapse file tree

4 files changed

+136
-5
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

4 files changed

+136
-5
lines changed
Open diff view settings
Collapse file

‎doc/api/util.md‎

Copy file name to clipboardExpand all lines: doc/api/util.md
+45Lines changed: 45 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1983,6 +1983,51 @@ const channel = new MessageChannel();
19831983
channel.port2.postMessage(signal, [signal]);
19841984
```
19851985
1986+
## `util.aborted(signal, resource)`
1987+
1988+
<!-- YAML
1989+
added: REPLACEME
1990+
-->
1991+
1992+
> Stability: 1 - Experimental
1993+
1994+
* `signal` {AbortSignal}
1995+
* `resource` {Object} Any non-null entity, reference to which is held weakly.
1996+
* Returns: {Promise}
1997+
1998+
Listens to abort event on the provided `signal` and
1999+
returns a promise that is fulfilled when the `signal` is
2000+
aborted. If the passed `resource` is garbage collected before the `signal` is
2001+
aborted, the returned promise shall remain pending indefinitely.
2002+
2003+
```cjs
2004+
const { aborted } = require('node:util');
2005+
2006+
const dependent = obtainSomethingAbortable();
2007+
2008+
aborted(dependent.signal, dependent).then(() => {
2009+
// Do something when dependent is aborted.
2010+
});
2011+
2012+
dependent.on('event', () => {
2013+
dependent.abort();
2014+
});
2015+
```
2016+
2017+
```mjs
2018+
import { aborted } from 'node:util';
2019+
2020+
const dependent = obtainSomethingAbortable();
2021+
2022+
aborted(dependent.signal, dependent).then(() => {
2023+
// Do something when dependent is aborted.
2024+
});
2025+
2026+
dependent.on('event', () => {
2027+
dependent.abort();
2028+
});
2029+
```
2030+
19862031
## `util.types`
19872032
19882033
<!-- YAML
Collapse file

‎lib/internal/abort_controller.js‎

Copy file name to clipboardExpand all lines: lib/internal/abort_controller.js
+29-5Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const {
88
ObjectDefineProperties,
99
ObjectSetPrototypeOf,
1010
ObjectDefineProperty,
11+
PromiseResolve,
1112
SafeFinalizationRegistry,
1213
SafeSet,
1314
Symbol,
@@ -22,11 +23,13 @@ const {
2223
kTrustEvent,
2324
kNewListener,
2425
kRemoveListener,
26+
kWeakHandler,
2527
} = require('internal/event_target');
2628
const {
29+
createDeferredPromise,
2730
customInspectSymbol,
28-
kEnumerableProperty,
2931
kEmptyObject,
32+
kEnumerableProperty,
3033
} = require('internal/util');
3134
const { inspect } = require('internal/util/inspect');
3235
const {
@@ -38,6 +41,8 @@ const {
3841
} = require('internal/errors');
3942

4043
const {
44+
validateAbortSignal,
45+
validateObject,
4146
validateUint32,
4247
} = require('internal/validators');
4348

@@ -94,7 +99,7 @@ function customInspect(self, obj, depth, options) {
9499
return `${self.constructor.name} ${inspect(obj, opts)}`;
95100
}
96101

97-
function validateAbortSignal(obj) {
102+
function validateThisAbortSignal(obj) {
98103
if (obj?.[kAborted] === undefined)
99104
throw new ERR_INVALID_THIS('AbortSignal');
100105
}
@@ -132,15 +137,15 @@ class AbortSignal extends EventTarget {
132137
* @type {boolean}
133138
*/
134139
get aborted() {
135-
validateAbortSignal(this);
140+
validateThisAbortSignal(this);
136141
return !!this[kAborted];
137142
}
138143

139144
/**
140145
* @type {any}
141146
*/
142147
get reason() {
143-
validateAbortSignal(this);
148+
validateThisAbortSignal(this);
144149
return this[kReason];
145150
}
146151

@@ -202,7 +207,7 @@ class AbortSignal extends EventTarget {
202207
}
203208

204209
[kTransfer]() {
205-
validateAbortSignal(this);
210+
validateThisAbortSignal(this);
206211
const aborted = this.aborted;
207212
if (aborted) {
208213
const reason = this.reason;
@@ -357,6 +362,24 @@ function transferableAbortController() {
357362
return AbortController[kMakeTransferable]();
358363
}
359364

365+
/**
366+
* @param {AbortSignal} signal
367+
* @param {any} resource
368+
* @returns {Promise<void>}
369+
*/
370+
async function aborted(signal, resource) {
371+
if (signal === undefined) {
372+
throw new ERR_INVALID_ARG_TYPE('signal', 'AbortSignal', signal);
373+
}
374+
validateAbortSignal(signal, 'signal');
375+
validateObject(resource, 'resource', { nullable: false, allowFunction: true, allowArray: true });
376+
if (signal.aborted)
377+
return PromiseResolve();
378+
const abortPromise = createDeferredPromise();
379+
signal.addEventListener('abort', abortPromise.resolve, { [kWeakHandler]: resource, once: true });
380+
return abortPromise.promise;
381+
}
382+
360383
ObjectDefineProperties(AbortController.prototype, {
361384
signal: kEnumerableProperty,
362385
abort: kEnumerableProperty,
@@ -375,6 +398,7 @@ module.exports = {
375398
AbortController,
376399
AbortSignal,
377400
ClonedAbortSignal,
401+
aborted,
378402
transferableAbortSignal,
379403
transferableAbortController,
380404
};
Collapse file

‎lib/util.js‎

Copy file name to clipboardExpand all lines: lib/util.js
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,9 @@ module.exports = {
393393
get transferableAbortController() {
394394
return lazyAbortController().transferableAbortController;
395395
},
396+
get aborted() {
397+
return lazyAbortController().aborted;
398+
},
396399
types
397400
};
398401

Collapse file

‎test/parallel/test-aborted-util.js‎

Copy file name to clipboard
+59Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Flags: --expose-gc
2+
'use strict';
3+
4+
const common = require('../common');
5+
const { aborted } = require('util');
6+
const assert = require('assert');
7+
const { getEventListeners } = require('events');
8+
const { spawn } = require('child_process');
9+
10+
{
11+
// Test aborted works when provided a resource
12+
const ac = new AbortController();
13+
aborted(ac.signal, {}).then(common.mustCall());
14+
ac.abort();
15+
assert.strictEqual(ac.signal.aborted, true);
16+
assert.strictEqual(getEventListeners(ac.signal, 'abort').length, 0);
17+
}
18+
19+
{
20+
// Test aborted with gc cleanup
21+
const ac = new AbortController();
22+
aborted(ac.signal, {}).then(common.mustNotCall());
23+
setImmediate(() => {
24+
global.gc();
25+
ac.abort();
26+
assert.strictEqual(ac.signal.aborted, true);
27+
assert.strictEqual(getEventListeners(ac.signal, 'abort').length, 0);
28+
});
29+
}
30+
31+
{
32+
// Fails with error if not provided abort signal
33+
Promise.all([{}, null, undefined, Symbol(), [], 1, 0, 1n, true, false, 'a', () => {}].map((sig) =>
34+
assert.rejects(aborted(sig, {}), {
35+
code: 'ERR_INVALID_ARG_TYPE',
36+
})
37+
)).then(common.mustCall());
38+
}
39+
40+
{
41+
// Fails if not provided a resource
42+
const ac = new AbortController();
43+
Promise.all([null, undefined, 0, 1, 0n, 1n, Symbol(), '', 'a'].map((resource) =>
44+
assert.rejects(aborted(ac.signal, resource), {
45+
code: 'ERR_INVALID_ARG_TYPE',
46+
})
47+
)).then(common.mustCall());
48+
}
49+
50+
{
51+
const childProcess = spawn(process.execPath, ['--input-type=module']);
52+
childProcess.on('exit', common.mustCall((code) => {
53+
assert.strictEqual(code, 13);
54+
}));
55+
childProcess.stdin.end(`
56+
import { aborted } from 'node:util';
57+
await aborted(new AbortController().signal, {});
58+
`);
59+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.