| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 4128c27 commit 7cff1e1
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -116,17 +116,35 @@ Each instance of `AsyncLocalStorage` maintains an independent storage context. | |||
| 116 | 116 | Multiple instances can safely exist simultaneously without risk of interfering | |
| 117 | 117 | with each other's data. | |
| 118 | 118 | ||
| 119 | - ### `new AsyncLocalStorage()` | ||
| 119 | + ### `new AsyncLocalStorage([options])` | ||
| 120 | 120 | ||
| 121 | 121 | <!-- YAML | |
| 122 | 122 | added: | |
| 123 | 123 | - v13.10.0 | |
| 124 | 124 | - v12.17.0 | |
| 125 | + changes: | ||
| 126 | + - version: REPLACEME | ||
| 127 | + pr-url: https://github.com/nodejs/node/pull/45386 | ||
| 128 | + description: Add option onPropagate. | ||
| 125 | 129 | --> | |
| 126 | 130 | ||
| 131 | + > Stability: 1 - `options.onPropagate` is experimental. | ||
| 132 | + | ||
| 133 | + * `options` {Object} | ||
| 134 | + * `onPropagate` {Function} Optional callback invoked before a store is | ||
| 135 | + propagated to a new async resource. Returning `true` allows propagation, | ||
| 136 | + returning `false` avoids it. Default is to propagate always. | ||
| 137 | + | ||
| 127 | 138 | Creates a new instance of `AsyncLocalStorage`. Store is only provided within a | |
| 128 | 139 | `run()` call or after an `enterWith()` call. | |
| 129 | 140 | ||
| 141 | + The `onPropagate` is called during creation of an async resource. Throwing at | ||
| 142 | + this time will print the stack trace and exit. See | ||
| 143 | + [`async_hooks` Error handling][] for details. | ||
| 144 | + | ||
| 145 | + Creating an async resource within the `onPropagate` callback will result in | ||
| 146 | + a recursive call to `onPropagate`. | ||
| 147 | + | ||
| 130 | 148 | ### `asyncLocalStorage.disable()` | |
| 131 | 149 | ||
| 132 | 150 | <!-- YAML | |
@@ -816,4 +834,5 @@ const server = createServer((req, res) => { | |||
| 816 | 834 | [`EventEmitter`]: events.md#class-eventemitter | |
| 817 | 835 | [`Stream`]: stream.md#stream | |
| 818 | 836 | [`Worker`]: worker_threads.md#class-worker | |
| 837 | + [`async_hooks` Error handling]: async_hooks.md#error-handling | ||
| 819 | 838 | [`util.promisify()`]: util.md#utilpromisifyoriginal | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,6 +18,7 @@ const { | |||
| 18 | 18 | const { | |
| 19 | 19 | ERR_ASYNC_CALLBACK, | |
| 20 | 20 | ERR_ASYNC_TYPE, | |
| 21 | + ERR_INVALID_ARG_TYPE, | ||
| 21 | 22 | ERR_INVALID_ASYNC_ID | |
| 22 | 23 | } = require('internal/errors').codes; | |
| 23 | 24 | const { kEmptyObject } = require('internal/util'); | |
@@ -268,15 +269,27 @@ const storageHook = createHook({ | |||
| 268 | 269 | const currentResource = executionAsyncResource(); | |
| 269 | 270 | // Value of currentResource is always a non null object | |
| 270 | 271 | for (let i = 0; i < storageList.length; ++i) { | |
| 271 | - storageList[i]._propagate(resource, currentResource); | ||
| 272 | + storageList[i]._propagate(resource, currentResource, type); | ||
| 272 | 273 | } | |
| 273 | 274 | } | |
| 274 | 275 | }); | |
| 275 | 276 | ||
| 276 | 277 | class AsyncLocalStorage { | |
| 277 | - constructor() { | ||
| 278 | + constructor(options = kEmptyObject) { | ||
| 279 | + if (typeof options !== 'object' || options === null) { | ||
| 280 | + throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); | ||
| 281 | + } | ||
| 282 | + | ||
| 283 | + const { onPropagate = null } = options; | ||
| 284 | + if (onPropagate !== null && typeof onPropagate !== 'function') { | ||
| 285 | + throw new ERR_INVALID_ARG_TYPE('options.onPropagate', | ||
| 286 | + 'function', | ||
| 287 | + onPropagate); | ||
| 288 | + } | ||
| 289 | + | ||
| 278 | 290 | this.kResourceStore = Symbol('kResourceStore'); | |
| 279 | 291 | this.enabled = false; | |
| 292 | + this._onPropagate = onPropagate; | ||
| 280 | 293 | } | |
| 281 | 294 | ||
| 282 | 295 | disable() { | |
@@ -300,10 +313,12 @@ class AsyncLocalStorage { | |||
| 300 | 313 | } | |
| 301 | 314 | ||
| 302 | 315 | // Propagate the context from a parent resource to a child one | |
| 303 | - _propagate(resource, triggerResource) { | ||
| 316 | + _propagate(resource, triggerResource, type) { | ||
| 304 | 317 | const store = triggerResource[this.kResourceStore]; | |
| 305 | 318 | if (this.enabled) { | |
| 306 | - resource[this.kResourceStore] = store; | ||
| 319 | + if (this._onPropagate === null || this._onPropagate(type, store)) { | ||
| 320 | + resource[this.kResourceStore] = store; | ||
| 321 | + } | ||
| 307 | 322 | } | |
| 308 | 323 | } | |
| 309 | 324 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,50 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + const common = require('../common'); | ||
| 3 | + const assert = require('assert'); | ||
| 4 | + const { AsyncLocalStorage, AsyncResource } = require('async_hooks'); | ||
| 5 | + | ||
| 6 | + let cnt = 0; | ||
| 7 | + function onPropagate(type, store) { | ||
| 8 | + assert.strictEqual(als.getStore(), store); | ||
| 9 | + cnt++; | ||
| 10 | + if (cnt === 1) { | ||
| 11 | + assert.strictEqual(type, 'r1'); | ||
| 12 | + return true; | ||
| 13 | + } | ||
| 14 | + if (cnt === 2) { | ||
| 15 | + assert.strictEqual(type, 'r2'); | ||
| 16 | + return false; | ||
| 17 | + } | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + const als = new AsyncLocalStorage({ | ||
| 21 | + onPropagate: common.mustCall(onPropagate, 2) | ||
| 22 | + }); | ||
| 23 | + | ||
| 24 | + const myStore = {}; | ||
| 25 | + | ||
| 26 | + als.run(myStore, common.mustCall(() => { | ||
| 27 | + const r1 = new AsyncResource('r1'); | ||
| 28 | + const r2 = new AsyncResource('r2'); | ||
| 29 | + r1.runInAsyncScope(common.mustCall(() => { | ||
| 30 | + assert.strictEqual(als.getStore(), myStore); | ||
| 31 | + })); | ||
| 32 | + r2.runInAsyncScope(common.mustCall(() => { | ||
| 33 | + assert.strictEqual(als.getStore(), undefined); | ||
| 34 | + r1.runInAsyncScope(common.mustCall(() => { | ||
| 35 | + assert.strictEqual(als.getStore(), myStore); | ||
| 36 | + })); | ||
| 37 | + })); | ||
| 38 | + })); | ||
| 39 | + | ||
| 40 | + assert.throws(() => new AsyncLocalStorage(15), { | ||
| 41 | + message: 'The "options" argument must be of type object. Received type number (15)', | ||
| 42 | + code: 'ERR_INVALID_ARG_TYPE', | ||
| 43 | + name: 'TypeError' | ||
| 44 | + }); | ||
| 45 | + | ||
| 46 | + assert.throws(() => new AsyncLocalStorage({ onPropagate: 'bar' }), { | ||
| 47 | + message: 'The "options.onPropagate" property must be of type function. Received type string (\'bar\')', | ||
| 48 | + code: 'ERR_INVALID_ARG_TYPE', | ||
| 49 | + name: 'TypeError' | ||
| 50 | + }); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments