async_hooks: merge run and exit methods

PR-URL: https://github.com/nodejs/node/pull/31950
Reviewed-By: Vladimir de Turckheim <vlad2t@hotmail.com>
Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de>
This commit is contained in:
Andrey Pechkurov 2020-03-14 09:06:06 +03:00 committed by Anna Henningsen
parent f7f0441997
commit 50dd63e8ef
No known key found for this signature in database
GPG Key ID: A94130F0BFC8EBE9
12 changed files with 30 additions and 215 deletions

View File

@ -101,7 +101,7 @@ function buildDestroy(getServe) {
function buildAsyncLocalStorage(getServe) {
const asyncLocalStorage = new AsyncLocalStorage();
const server = createServer((req, res) => {
asyncLocalStorage.runSyncAndReturn({}, () => {
asyncLocalStorage.run({}, () => {
getServe(getCLS, setCLS)(req, res);
});
});

View File

@ -922,7 +922,7 @@ added: v13.10.0
-->
Creates a new instance of `AsyncLocalStorage`. Store is only provided within a
`run` or a `runSyncAndReturn` method call.
`run` method call.
### `asyncLocalStorage.disable()`
<!-- YAML
@ -931,8 +931,7 @@ added: v13.10.0
This method disables the instance of `AsyncLocalStorage`. All subsequent calls
to `asyncLocalStorage.getStore()` will return `undefined` until
`asyncLocalStorage.run()` or `asyncLocalStorage.runSyncAndReturn()`
is called again.
`asyncLocalStorage.run()` is called again.
When calling `asyncLocalStorage.disable()`, all current contexts linked to the
instance will be exited.
@ -954,8 +953,7 @@ added: v13.10.0
This method returns the current store.
If this method is called outside of an asynchronous context initialized by
calling `asyncLocalStorage.run` or `asyncLocalStorage.runSyncAndReturn`, it will
return `undefined`.
calling `asyncLocalStorage.run`, it will return `undefined`.
### `asyncLocalStorage.enterWith(store)`
<!-- YAML
@ -1008,73 +1006,6 @@ added: v13.10.0
* `callback` {Function}
* `...args` {any}
Calling `asyncLocalStorage.run(callback)` will create a new asynchronous
context. Within the callback function and the asynchronous operations from
the callback, `asyncLocalStorage.getStore()` will return the object or
the primitive value passed into the `store` argument (known as "the store").
This store will be persistent through the following asynchronous calls.
The callback will be ran asynchronously. Optionally, arguments can be passed
to the function. They will be passed to the callback function.
If an error is thrown by the callback function, it will not be caught by
a `try/catch` block as the callback is ran in a new asynchronous resource.
Also, the stacktrace will be impacted by the asynchronous call.
Example:
```js
const store = { id: 1 };
asyncLocalStorage.run(store, () => {
asyncLocalStorage.getStore(); // Returns the store object
someAsyncOperation(() => {
asyncLocalStorage.getStore(); // Returns the same object
});
});
asyncLocalStorage.getStore(); // Returns undefined
```
### `asyncLocalStorage.exit(callback[, ...args])`
<!-- YAML
added: v13.10.0
-->
* `callback` {Function}
* `...args` {any}
Calling `asyncLocalStorage.exit(callback)` will create a new asynchronous
context.
Within the callback function and the asynchronous operations from the callback,
`asyncLocalStorage.getStore()` will return `undefined`.
The callback will be ran asynchronously. Optionally, arguments can be passed
to the function. They will be passed to the callback function.
If an error is thrown by the callback function, it will not be caught by
a `try/catch` block as the callback is ran in a new asynchronous resource.
Also, the stacktrace will be impacted by the asynchronous call.
Example:
```js
asyncLocalStorage.run('store value', () => {
asyncLocalStorage.getStore(); // Returns 'store value'
asyncLocalStorage.exit(() => {
asyncLocalStorage.getStore(); // Returns undefined
});
asyncLocalStorage.getStore(); // Returns 'store value'
});
```
### `asyncLocalStorage.runSyncAndReturn(store, callback[, ...args])`
<!-- YAML
added: v13.10.0
-->
* `store` {any}
* `callback` {Function}
* `...args` {any}
This methods runs a function synchronously within a context and return its
return value. The store is not accessible outside of the callback function or
the asynchronous operations created within the callback.
@ -1082,16 +1013,16 @@ the asynchronous operations created within the callback.
Optionally, arguments can be passed to the function. They will be passed to
the callback function.
If the callback function throws an error, it will be thrown by
`runSyncAndReturn` too. The stacktrace will not be impacted by this call and
the context will be exited.
If the callback function throws an error, it will be thrown by `run` too.
The stacktrace will not be impacted by this call and the context will
be exited.
Example:
```js
const store = { id: 2 };
try {
asyncLocalStorage.runSyncAndReturn(store, () => {
asyncLocalStorage.run(store, () => {
asyncLocalStorage.getStore(); // Returns the store object
throw new Error();
});
@ -1101,7 +1032,7 @@ try {
}
```
### `asyncLocalStorage.exitSyncAndReturn(callback[, ...args])`
### `asyncLocalStorage.exit(callback[, ...args])`
<!-- YAML
added: v13.10.0
-->
@ -1116,17 +1047,17 @@ the asynchronous operations created within the callback.
Optionally, arguments can be passed to the function. They will be passed to
the callback function.
If the callback function throws an error, it will be thrown by
`exitSyncAndReturn` too. The stacktrace will not be impacted by this call and
If the callback function throws an error, it will be thrown by `exit` too.
The stacktrace will not be impacted by this call and
the context will be re-entered.
Example:
```js
// Within a call to run or runSyncAndReturn
// Within a call to run
try {
asyncLocalStorage.getStore(); // Returns the store object or value
asyncLocalStorage.exitSyncAndReturn(() => {
asyncLocalStorage.exit(() => {
asyncLocalStorage.getStore(); // Returns undefined
throw new Error();
});
@ -1136,59 +1067,14 @@ try {
}
```
### Choosing between `run` and `runSyncAndReturn`
#### When to choose `run`
`run` is asynchronous. It is called with a callback function that
runs within a new asynchronous call. This is the most explicit behavior as
everything that is executed within the callback of `run` (including further
asynchronous operations) will have access to the store.
If an instance of `AsyncLocalStorage` is used for error management (for
instance, with `process.setUncaughtExceptionCaptureCallback`), only
exceptions thrown in the scope of the callback function will be associated
with the context.
This method is the safest as it provides strong scoping and consistent
behavior.
It cannot be promisified using `util.promisify`. If needed, the `Promise`
constructor can be used:
```js
const store = new Map(); // initialize the store
new Promise((resolve, reject) => {
asyncLocalStorage.run(store, () => {
someFunction((err, result) => {
if (err) {
return reject(err);
}
return resolve(result);
});
});
});
```
#### When to choose `runSyncAndReturn`
`runSyncAndReturn` is synchronous. The callback function will be executed
synchronously and its return value will be returned by `runSyncAndReturn`.
The store will only be accessible from within the callback
function and the asynchronous operations created within this scope.
If the callback throws an error, `runSyncAndReturn` will throw it and it will
not be associated with the context.
This method provides good scoping while being synchronous.
#### Usage with `async/await`
### Usage with `async/await`
If, within an async function, only one `await` call is to run within a context,
the following pattern should be used:
```js
async function fn() {
await asyncLocalStorage.runSyncAndReturn(new Map(), () => {
await asyncLocalStorage.run(new Map(), () => {
asyncLocalStorage.getStore().set('key', value);
return foo(); // The return value of foo will be awaited
});
@ -1196,8 +1082,8 @@ async function fn() {
```
In this example, the store is only available in the callback function and the
functions called by `foo`. Outside of `runSyncAndReturn`, calling `getStore`
will return `undefined`.
functions called by `foo`. Outside of `run`, calling `getStore` will return
`undefined`.
[`after` callback]: #async_hooks_after_asyncid
[`before` callback]: #async_hooks_before_asyncid

View File

@ -256,7 +256,7 @@ class AsyncLocalStorage {
resource[this.kResourceStore] = store;
}
runSyncAndReturn(store, callback, ...args) {
run(store, callback, ...args) {
const resource = new AsyncResource('AsyncLocalStorage');
return resource.runInAsyncScope(() => {
this.enterWith(store);
@ -264,7 +264,7 @@ class AsyncLocalStorage {
});
}
exitSyncAndReturn(callback, ...args) {
exit(callback, ...args) {
if (!this.enabled) {
return callback(...args);
}
@ -282,22 +282,6 @@ class AsyncLocalStorage {
return resource[this.kResourceStore];
}
}
run(store, callback, ...args) {
process.nextTick(() => {
this.enterWith(store);
return callback(...args);
});
}
exit(callback, ...args) {
if (!this.enabled) {
return process.nextTick(callback, ...args);
}
this.enabled = false;
process.nextTick(callback, ...args);
this.enabled = true;
}
}
// Placing all exports down here because the exported classes won't export

View File

@ -6,15 +6,8 @@ const { AsyncLocalStorage } = require('async_hooks');
const asyncLocalStorage = new AsyncLocalStorage();
asyncLocalStorage.run({}, (runArg) => {
assert.strictEqual(runArg, 1);
asyncLocalStorage.exit((exitArg) => {
assert.strictEqual(exitArg, 2);
}, 2);
}, 1);
asyncLocalStorage.runSyncAndReturn({}, (runArg) => {
assert.strictEqual(runArg, 'foo');
asyncLocalStorage.exitSyncAndReturn((exitArg) => {
asyncLocalStorage.exit((exitArg) => {
assert.strictEqual(exitArg, 'bar');
}, 'bar');
}, 'foo');

View File

@ -12,7 +12,7 @@ async function test() {
}
async function main() {
await asyncLocalStorage.runSyncAndReturn(new Map(), test);
await asyncLocalStorage.run(new Map(), test);
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
}

View File

@ -16,7 +16,7 @@ async function testAwait() {
await foo();
assert.notStrictEqual(asyncLocalStorage.getStore(), undefined);
assert.strictEqual(asyncLocalStorage.getStore().get('key'), 'value');
await asyncLocalStorage.exitSyncAndReturn(testOut);
await asyncLocalStorage.exit(testOut);
}
asyncLocalStorage.run(new Map(), () => {

View File

@ -5,7 +5,7 @@ const { AsyncLocalStorage } = require('async_hooks');
const asyncLocalStorage = new AsyncLocalStorage();
asyncLocalStorage.runSyncAndReturn(new Map(), () => {
asyncLocalStorage.run(new Map(), () => {
asyncLocalStorage.getStore().set('foo', 'bar');
process.nextTick(() => {
assert.strictEqual(asyncLocalStorage.getStore().get('foo'), 'bar');
@ -24,7 +24,7 @@ asyncLocalStorage.runSyncAndReturn(new Map(), () => {
process.nextTick(() => {
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
asyncLocalStorage.runSyncAndReturn(new Map(), () => {
asyncLocalStorage.run(new Map(), () => {
assert.notStrictEqual(asyncLocalStorage.getStore(), undefined);
});
});

View File

@ -1,26 +0,0 @@
'use strict';
require('../common');
const assert = require('assert');
const { AsyncLocalStorage } = require('async_hooks');
// case 1 fully async APIS (safe)
const asyncLocalStorage = new AsyncLocalStorage();
let i = 0;
process.setUncaughtExceptionCaptureCallback((err) => {
++i;
assert.strictEqual(err.message, 'err' + i);
assert.strictEqual(asyncLocalStorage.getStore().get('hello'), 'node');
});
asyncLocalStorage.run(new Map(), () => {
const store = asyncLocalStorage.getStore();
store.set('hello', 'node');
setTimeout(() => {
process.nextTick(() => {
assert.strictEqual(i, 2);
});
throw new Error('err2');
}, 0);
throw new Error('err1');
});

View File

@ -14,7 +14,7 @@ process.setUncaughtExceptionCaptureCallback((err) => {
});
try {
asyncLocalStorage.runSyncAndReturn(new Map(), () => {
asyncLocalStorage.run(new Map(), () => {
const store = asyncLocalStorage.getStore();
store.set('hello', 'node');
setTimeout(() => {

View File

@ -10,7 +10,7 @@ const onGC = require('../common/ongc');
let asyncLocalStorage = new AsyncLocalStorage();
asyncLocalStorage.runSyncAndReturn({}, () => {
asyncLocalStorage.run({}, () => {
asyncLocalStorage.disable();
onGC(asyncLocalStorage, { ongc: common.mustCall() });

View File

@ -5,20 +5,11 @@ const { AsyncLocalStorage } = require('async_hooks');
const asyncLocalStorage = new AsyncLocalStorage();
asyncLocalStorage.run(42, () => {
assert.strictEqual(asyncLocalStorage.getStore(), 42);
});
const runStore = { foo: 'bar' };
asyncLocalStorage.run(runStore, () => {
assert.strictEqual(asyncLocalStorage.getStore(), runStore);
});
asyncLocalStorage.runSyncAndReturn('hello node', () => {
asyncLocalStorage.run('hello node', () => {
assert.strictEqual(asyncLocalStorage.getStore(), 'hello node');
});
const runSyncStore = { hello: 'node' };
asyncLocalStorage.runSyncAndReturn(runSyncStore, () => {
assert.strictEqual(asyncLocalStorage.getStore(), runSyncStore);
const runStore = { hello: 'node' };
asyncLocalStorage.run(runStore, () => {
assert.strictEqual(asyncLocalStorage.getStore(), runStore);
});

View File

@ -19,20 +19,7 @@ function testInner() {
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
});
assert.strictEqual(asyncLocalStorage.getStore(), outer);
asyncLocalStorage.runSyncAndReturn(inner, () => {
assert.strictEqual(asyncLocalStorage.getStore(), inner);
});
assert.strictEqual(asyncLocalStorage.getStore(), outer);
asyncLocalStorage.exitSyncAndReturn(() => {
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
});
assert.strictEqual(asyncLocalStorage.getStore(), outer);
}
asyncLocalStorage.run(outer, testInner);
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
asyncLocalStorage.runSyncAndReturn(outer, testInner);
assert.strictEqual(asyncLocalStorage.getStore(), undefined);