async_hooks: add trackPromises option to createHook() · nodejs/node@8b6d31d · GitHub
Skip to content

Commit 8b6d31d

Browse files
joyeecheungaduh95
authored andcommitted
async_hooks: add trackPromises option to createHook()
This adds a trackPromises option that allows users to completely opt out of the promise hooks that are installed whenever an async hook is added. For those who do not need to track promises, this avoids the excessive hook invocation and the heavy overhead from it. This option was previously already implemented internally to skip the noise from promise hooks when debugging async operations via the V8 inspector. This patch just exposes it. PR-URL: #61415 Refs: #57148 Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de>
1 parent 7824c75 commit 8b6d31d

9 files changed

Lines changed: 145 additions & 9 deletions

doc/api/async_hooks.md

Lines changed: 39 additions & 4 deletions

lib/async_hooks.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ const {
1818
ERR_ASYNC_CALLBACK,
1919
ERR_ASYNC_TYPE,
2020
ERR_INVALID_ASYNC_ID,
21+
ERR_INVALID_ARG_TYPE,
22+
ERR_INVALID_ARG_VALUE,
2123
} = require('internal/errors').codes;
2224
const {
2325
deprecate,
@@ -73,7 +75,7 @@ const {
7375
// Listener API //
7476

7577
class AsyncHook {
76-
constructor({ init, before, after, destroy, promiseResolve }) {
78+
constructor({ init, before, after, destroy, promiseResolve, trackPromises }) {
7779
if (init !== undefined && typeof init !== 'function')
7880
throw new ERR_ASYNC_CALLBACK('hook.init');
7981
if (before !== undefined && typeof before !== 'function')
@@ -84,13 +86,25 @@ class AsyncHook {
8486
throw new ERR_ASYNC_CALLBACK('hook.destroy');
8587
if (promiseResolve !== undefined && typeof promiseResolve !== 'function')
8688
throw new ERR_ASYNC_CALLBACK('hook.promiseResolve');
89+
if (trackPromises !== undefined && typeof trackPromises !== 'boolean') {
90+
throw new ERR_INVALID_ARG_TYPE('trackPromises', 'boolean', trackPromises);
91+
}
8792

8893
this[init_symbol] = init;
8994
this[before_symbol] = before;
9095
this[after_symbol] = after;
9196
this[destroy_symbol] = destroy;
9297
this[promise_resolve_symbol] = promiseResolve;
93-
this[kNoPromiseHook] = false;
98+
if (trackPromises === false) {
99+
if (promiseResolve) {
100+
throw new ERR_INVALID_ARG_VALUE('trackPromises',
101+
trackPromises, 'must not be false when promiseResolve is enabled');
102+
}
103+
this[kNoPromiseHook] = true;
104+
} else {
105+
// Default to tracking promises for now.
106+
this[kNoPromiseHook] = false;
107+
}
94108
}
95109

96110
enable() {

lib/internal/inspector_async_hook.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ function lazyHookCreation() {
77
const inspector = internalBinding('inspector');
88
const { createHook } = require('async_hooks');
99
config = internalBinding('config');
10-
const { kNoPromiseHook } = require('internal/async_hooks');
1110

1211
hook = createHook({
1312
init(asyncId, type, triggerAsyncId, resource) {
@@ -30,8 +29,8 @@ function lazyHookCreation() {
3029
destroy(asyncId) {
3130
inspector.asyncTaskCanceled(asyncId);
3231
},
32+
trackPromises: false,
3333
});
34-
hook[kNoPromiseHook] = true;
3534
}
3635

3736
function enable() {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use strict';
2+
// Test that trackPromises default to true.
3+
const common = require('../common');
4+
const { createHook } = require('node:async_hooks');
5+
const assert = require('node:assert');
6+
7+
let res;
8+
createHook({
9+
init: common.mustCall((asyncId, type, triggerAsyncId, resource) => {
10+
assert.strictEqual(type, 'PROMISE');
11+
res = resource;
12+
}),
13+
}).enable();
14+
15+
const promise = Promise.resolve(1729);
16+
assert.strictEqual(res, promise);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
// Test that trackPromises: false prevents promise hooks from being installed.
4+
5+
require('../common');
6+
const { internalBinding } = require('internal/test/binding');
7+
const { getPromiseHooks } = internalBinding('async_wrap');
8+
const { createHook } = require('node:async_hooks');
9+
const assert = require('node:assert');
10+
11+
createHook({
12+
init() {
13+
// This can get called for writes to stdout due to the warning about internals.
14+
},
15+
trackPromises: false,
16+
}).enable();
17+
18+
Promise.resolve(1729);
19+
assert.deepStrictEqual(getPromiseHooks(), [undefined, undefined, undefined, undefined]);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use strict';
2+
// Test that trackPromises: false works.
3+
const common = require('../common');
4+
const { createHook } = require('node:async_hooks');
5+
6+
createHook({
7+
init: common.mustNotCall(),
8+
trackPromises: false,
9+
}).enable();
10+
11+
Promise.resolve(1729);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
// Test that trackPromises: true works.
3+
const common = require('../common');
4+
const { createHook } = require('node:async_hooks');
5+
const assert = require('node:assert');
6+
7+
let res;
8+
createHook({
9+
init: common.mustCall((asyncId, type, triggerAsyncId, resource) => {
10+
assert.strictEqual(type, 'PROMISE');
11+
res = resource;
12+
}),
13+
trackPromises: true,
14+
}).enable();
15+
16+
const promise = Promise.resolve(1729);
17+
assert.strictEqual(res, promise);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict';
2+
// Test validation of trackPromises option.
3+
4+
require('../common');
5+
const { createHook } = require('node:async_hooks');
6+
const assert = require('node:assert');
7+
const { inspect } = require('util');
8+
9+
for (const invalid of [0, null, 1, NaN, Symbol(0), function() {}, 'test']) {
10+
assert.throws(
11+
() => createHook({
12+
init() {},
13+
trackPromises: invalid,
14+
}),
15+
{ code: 'ERR_INVALID_ARG_TYPE' },
16+
`trackPromises: ${inspect(invalid)} should throw`);
17+
}
18+
19+
assert.throws(
20+
() => createHook({
21+
trackPromises: false,
22+
promiseResolve() {},
23+
}),
24+
{ code: 'ERR_INVALID_ARG_VALUE' },
25+
`trackPromises: false and promiseResolve() are incompatible`);

tools/doc/type-parser.mjs

Lines changed: 1 addition & 1 deletion

0 commit comments

Comments
 (0)