| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 8f2083e commit 796ff46
9 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -144,18 +144,20 @@ function destroy(asyncId) { } | |||
| 144 | 144 | function promiseResolve(asyncId) { } | |
| 145 | 145 | ``` | |
| 146 | 146 | ||
| 147 | - ## `async_hooks.createHook(callbacks)` | ||
| 147 | + ## `async_hooks.createHook(options)` | ||
| 148 | 148 | ||
| 149 | 149 | <!-- YAML | |
| 150 | 150 | added: v8.1.0 | |
| 151 | 151 | --> | |
| 152 | 152 | ||
| 153 | - * `callbacks` {Object} The [Hook Callbacks][] to register | ||
| 153 | + * `options` {Object} The [Hook Callbacks][] to register | ||
| 154 | 154 | * `init` {Function} The [`init` callback][]. | |
| 155 | 155 | * `before` {Function} The [`before` callback][]. | |
| 156 | 156 | * `after` {Function} The [`after` callback][]. | |
| 157 | 157 | * `destroy` {Function} The [`destroy` callback][]. | |
| 158 | 158 | * `promiseResolve` {Function} The [`promiseResolve` callback][]. | |
| 159 | + * `trackPromises` {boolean} Whether the hook should track `Promise`s. Cannot be `false` if | ||
| 160 | + `promiseResolve` is set. **Default**: `true`. | ||
| 159 | 161 | * Returns: {AsyncHook} Instance used for disabling and enabling hooks | |
| 160 | 162 | ||
| 161 | 163 | Registers functions to be called for different lifetime events of each async | |
@@ -354,7 +356,8 @@ Furthermore users of [`AsyncResource`][] create async resources independent | |||
| 354 | 356 | of Node.js itself. | |
| 355 | 357 | ||
| 356 | 358 | There is also the `PROMISE` resource type, which is used to track `Promise` | |
| 357 | - instances and asynchronous work scheduled by them. | ||
| 359 | + instances and asynchronous work scheduled by them. The `Promise`s are only | ||
| 360 | + tracked when `trackPromises` option is set to `true`. | ||
| 358 | 361 | ||
| 359 | 362 | Users are able to define their own `type` when using the public embedder API. | |
| 360 | 363 | ||
@@ -910,6 +913,38 @@ only on chained promises. That means promises not created by `then()`/`catch()` | |||
| 910 | 913 | will not have the `before` and `after` callbacks fired on them. For more details | |
| 911 | 914 | see the details of the V8 [PromiseHooks][] API. | |
| 912 | 915 | ||
| 916 | + ### Disabling promise execution tracking | ||
| 917 | + | ||
| 918 | + Tracking promise execution can cause a significant performance overhead. | ||
| 919 | + To opt out of promise tracking, set `trackPromises` to `false`: | ||
| 920 | + | ||
| 921 | + ```cjs | ||
| 922 | + const { createHook } = require('node:async_hooks'); | ||
| 923 | + const { writeSync } = require('node:fs'); | ||
| 924 | + createHook({ | ||
| 925 | + init(asyncId, type, triggerAsyncId, resource) { | ||
| 926 | + // This init hook does not get called when trackPromises is set to false. | ||
| 927 | + writeSync(1, `init hook triggered for ${type}\n`); | ||
| 928 | + }, | ||
| 929 | + trackPromises: false, // Do not track promises. | ||
| 930 | + }).enable(); | ||
| 931 | + Promise.resolve(1729); | ||
| 932 | + ``` | ||
| 933 | + | ||
| 934 | + ```mjs | ||
| 935 | + import { createHook } from 'node:async_hooks'; | ||
| 936 | + import { writeSync } from 'node:fs'; | ||
| 937 | + | ||
| 938 | + createHook({ | ||
| 939 | + init(asyncId, type, triggerAsyncId, resource) { | ||
| 940 | + // This init hook does not get called when trackPromises is set to false. | ||
| 941 | + writeSync(1, `init hook triggered for ${type}\n`); | ||
| 942 | + }, | ||
| 943 | + trackPromises: false, // Do not track promises. | ||
| 944 | + }).enable(); | ||
| 945 | + Promise.resolve(1729); | ||
| 946 | + ``` | ||
| 947 | + | ||
| 913 | 948 | ## JavaScript embedder API | |
| 914 | 949 | ||
| 915 | 950 | Library developers that handle their own asynchronous resources performing tasks | |
@@ -934,7 +969,7 @@ The documentation for this class has moved [`AsyncLocalStorage`][]. | |||
| 934 | 969 | [`Worker`]: worker_threads.md#class-worker | |
| 935 | 970 | [`after` callback]: #afterasyncid | |
| 936 | 971 | [`before` callback]: #beforeasyncid | |
| 937 | - [`createHook`]: #async_hookscreatehookcallbacks | ||
| 972 | + [`createHook`]: #async_hookscreatehookoptions | ||
| 938 | 973 | [`destroy` callback]: #destroyasyncid | |
| 939 | 974 | [`executionAsyncResource`]: #async_hooksexecutionasyncresource | |
| 940 | 975 | [`init` callback]: #initasyncid-type-triggerasyncid-resource | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,6 +18,8 @@ const { | |||
| 18 | 18 | ERR_ASYNC_CALLBACK, | |
| 19 | 19 | ERR_ASYNC_TYPE, | |
| 20 | 20 | ERR_INVALID_ASYNC_ID, | |
| 21 | + ERR_INVALID_ARG_TYPE, | ||
| 22 | + ERR_INVALID_ARG_VALUE, | ||
| 21 | 23 | } = require('internal/errors').codes; | |
| 22 | 24 | const { | |
| 23 | 25 | kEmptyObject, | |
@@ -71,7 +73,7 @@ const { | |||
| 71 | 73 | // Listener API // | |
| 72 | 74 | ||
| 73 | 75 | class AsyncHook { | |
| 74 | - constructor({ init, before, after, destroy, promiseResolve }) { | ||
| 76 | + constructor({ init, before, after, destroy, promiseResolve, trackPromises }) { | ||
| 75 | 77 | if (init !== undefined && typeof init !== 'function') | |
| 76 | 78 | throw new ERR_ASYNC_CALLBACK('hook.init'); | |
| 77 | 79 | if (before !== undefined && typeof before !== 'function') | |
@@ -82,13 +84,25 @@ class AsyncHook { | |||
| 82 | 84 | throw new ERR_ASYNC_CALLBACK('hook.destroy'); | |
| 83 | 85 | if (promiseResolve !== undefined && typeof promiseResolve !== 'function') | |
| 84 | 86 | throw new ERR_ASYNC_CALLBACK('hook.promiseResolve'); | |
| 87 | + if (trackPromises !== undefined && typeof trackPromises !== 'boolean') { | ||
| 88 | + throw new ERR_INVALID_ARG_TYPE('trackPromises', 'boolean', trackPromises); | ||
| 89 | + } | ||
| 85 | 90 | ||
| 86 | 91 | this[init_symbol] = init; | |
| 87 | 92 | this[before_symbol] = before; | |
| 88 | 93 | this[after_symbol] = after; | |
| 89 | 94 | this[destroy_symbol] = destroy; | |
| 90 | 95 | this[promise_resolve_symbol] = promiseResolve; | |
| 91 | - this[kNoPromiseHook] = false; | ||
| 96 | + if (trackPromises === false) { | ||
| 97 | + if (promiseResolve) { | ||
| 98 | + throw new ERR_INVALID_ARG_VALUE('trackPromises', | ||
| 99 | + trackPromises, 'must not be false when promiseResolve is enabled'); | ||
| 100 | + } | ||
| 101 | + this[kNoPromiseHook] = true; | ||
| 102 | + } else { | ||
| 103 | + // Default to tracking promises for now. | ||
| 104 | + this[kNoPromiseHook] = false; | ||
| 105 | + } | ||
| 92 | 106 | } | |
| 93 | 107 | ||
| 94 | 108 | enable() { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,7 +7,6 @@ function lazyHookCreation() { | |||
| 7 | 7 | const inspector = internalBinding('inspector'); | |
| 8 | 8 | const { createHook } = require('async_hooks'); | |
| 9 | 9 | config = internalBinding('config'); | |
| 10 | - const { kNoPromiseHook } = require('internal/async_hooks'); | ||
| 11 | 10 | ||
| 12 | 11 | hook = createHook({ | |
| 13 | 12 | init(asyncId, type, triggerAsyncId, resource) { | |
@@ -30,8 +29,8 @@ function lazyHookCreation() { | |||
| 30 | 29 | destroy(asyncId) { | |
| 31 | 30 | inspector.asyncTaskCanceled(asyncId); | |
| 32 | 31 | }, | |
| 32 | + trackPromises: false, | ||
| 33 | 33 | }); | |
| 34 | - hook[kNoPromiseHook] = true; | ||
| 35 | 34 | } | |
| 36 | 35 | ||
| 37 | 36 | function enable() { | |
| Original file line number | Diff line number | Diff 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); | ||
| Original file line number | Diff line number | Diff 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]); | ||
| Original file line number | Diff line number | Diff 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); | ||
| Original file line number | Diff line number | Diff 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); | ||
| Original file line number | Diff line number | Diff 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`); | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -66,7 +66,7 @@ const customTypesMap = { | |||
| 66 | 66 | ||
| 67 | 67 | 'AsyncLocalStorage': 'async_context.html#class-asynclocalstorage', | |
| 68 | 68 | ||
| 69 | - 'AsyncHook': 'async_hooks.html#async_hookscreatehookcallbacks', | ||
| 69 | + 'AsyncHook': 'async_hooks.html#async_hookscreatehookoptions', | ||
| 70 | 70 | 'AsyncResource': 'async_hooks.html#class-asyncresource', | |
| 71 | 71 | ||
| 72 | 72 | 'brotli options': 'zlib.html#class-brotlioptions', | |
| Back | FazBrowse Home | New Git URL |
0 commit comments