Skip to content

Commit 96d1f14

Browse files
puzpuzpuztargos
authored andcommitted
async_hooks: add store arg in AsyncLocalStorage
This commit introduces store as the first argument in AsyncLocalStorage's run methods. The change is motivated by the following expectation: most users are going to use a custom object as the store and an extra Map created by the previous implementation is an overhead for their use case. Important note. This is a backwards incompatible change. It was discussed and agreed an incompatible change is ok since the API is still experimental and the modified methods were only added within the last week so usage will be minimal to none. PR-URL: #31930 Reviewed-By: Stephen Belanger <[email protected]> Reviewed-By: Vladimir de Turckheim <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Michael Dawson <[email protected]>
1 parent 16e8b11 commit 96d1f14

14 files changed

+73
-47
lines changed

benchmark/async_hooks/async-resource-vs-destroy.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ function buildDestroy(getServe) {
106106
function buildAsyncLocalStorage(getServe) {
107107
const asyncLocalStorage = new AsyncLocalStorage();
108108
const server = createServer((req, res) => {
109-
asyncLocalStorage.runSyncAndReturn(() => {
109+
asyncLocalStorage.runSyncAndReturn({}, () => {
110110
getServe(getCLS, setCLS)(req, res);
111111
});
112112
});
@@ -118,12 +118,12 @@ function buildAsyncLocalStorage(getServe) {
118118

119119
function getCLS() {
120120
const store = asyncLocalStorage.getStore();
121-
return store.get('store');
121+
return store.state;
122122
}
123123

124124
function setCLS(state) {
125125
const store = asyncLocalStorage.getStore();
126-
store.set('store', state);
126+
store.state = state;
127127
}
128128

129129
function close() {

doc/api/async_hooks.md

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,7 @@ function log(...args) {
900900
}
901901

902902
http.createServer((request, response) => {
903-
asyncLocalStorage.run(() => {
903+
asyncLocalStorage.run(new Map(), () => {
904904
const store = asyncLocalStorage.getStore();
905905
store.set(kReq, request);
906906
someAsyncOperation((err, result) => {
@@ -950,27 +950,27 @@ in the current process.
950950
added: REPLACEME
951951
-->
952952

953-
* Returns: {Map}
953+
* Returns: {any}
954954

955955
This method returns the current store.
956956
If this method is called outside of an asynchronous context initialized by
957957
calling `asyncLocalStorage.run` or `asyncLocalStorage.runAndReturn`, it will
958958
return `undefined`.
959959

960-
### `asyncLocalStorage.run(callback[, ...args])`
960+
### `asyncLocalStorage.run(store, callback[, ...args])`
961961
<!-- YAML
962962
added: REPLACEME
963963
-->
964964

965+
* `store` {any}
965966
* `callback` {Function}
966967
* `...args` {any}
967968

968969
Calling `asyncLocalStorage.run(callback)` will create a new asynchronous
969-
context.
970-
Within the callback function and the asynchronous operations from the callback,
971-
`asyncLocalStorage.getStore()` will return an instance of `Map` known as
972-
"the store". This store will be persistent through the following
973-
asynchronous calls.
970+
context. Within the callback function and the asynchronous operations from
971+
the callback, `asyncLocalStorage.getStore()` will return the object or
972+
the primitive value passed into the `store` argument (known as "the store").
973+
This store will be persistent through the following asynchronous calls.
974974

975975
The callback will be ran asynchronously. Optionally, arguments can be passed
976976
to the function. They will be passed to the callback function.
@@ -982,10 +982,11 @@ Also, the stacktrace will be impacted by the asynchronous call.
982982
Example:
983983

984984
```js
985-
asyncLocalStorage.run(() => {
986-
asyncLocalStorage.getStore(); // Returns a Map
985+
const store = { id: 1 };
986+
asyncLocalStorage.run(store, () => {
987+
asyncLocalStorage.getStore(); // Returns the store object
987988
someAsyncOperation(() => {
988-
asyncLocalStorage.getStore(); // Returns the same Map
989+
asyncLocalStorage.getStore(); // Returns the same object
989990
});
990991
});
991992
asyncLocalStorage.getStore(); // Returns undefined
@@ -1014,20 +1015,21 @@ Also, the stacktrace will be impacted by the asynchronous call.
10141015
Example:
10151016

10161017
```js
1017-
asyncLocalStorage.run(() => {
1018-
asyncLocalStorage.getStore(); // Returns a Map
1018+
asyncLocalStorage.run('store value', () => {
1019+
asyncLocalStorage.getStore(); // Returns 'store value'
10191020
asyncLocalStorage.exit(() => {
10201021
asyncLocalStorage.getStore(); // Returns undefined
10211022
});
1022-
asyncLocalStorage.getStore(); // Returns the same Map
1023+
asyncLocalStorage.getStore(); // Returns 'store value'
10231024
});
10241025
```
10251026

1026-
### `asyncLocalStorage.runSyncAndReturn(callback[, ...args])`
1027+
### `asyncLocalStorage.runSyncAndReturn(store, callback[, ...args])`
10271028
<!-- YAML
10281029
added: REPLACEME
10291030
-->
10301031

1032+
* `store` {any}
10311033
* `callback` {Function}
10321034
* `...args` {any}
10331035

@@ -1045,9 +1047,10 @@ the context will be exited.
10451047
Example:
10461048

10471049
```js
1050+
const store = { id: 2 };
10481051
try {
1049-
asyncLocalStorage.runSyncAndReturn(() => {
1050-
asyncLocalStorage.getStore(); // Returns a Map
1052+
asyncLocalStorage.runSyncAndReturn(store, () => {
1053+
asyncLocalStorage.getStore(); // Returns the store object
10511054
throw new Error();
10521055
});
10531056
} catch (e) {
@@ -1080,13 +1083,13 @@ Example:
10801083
```js
10811084
// Within a call to run or runSyncAndReturn
10821085
try {
1083-
asyncLocalStorage.getStore(); // Returns a Map
1086+
asyncLocalStorage.getStore(); // Returns the store object or value
10841087
asyncLocalStorage.exitSyncAndReturn(() => {
10851088
asyncLocalStorage.getStore(); // Returns undefined
10861089
throw new Error();
10871090
});
10881091
} catch (e) {
1089-
asyncLocalStorage.getStore(); // Returns the same Map
1092+
asyncLocalStorage.getStore(); // Returns the same object or value
10901093
// The error will be caught here
10911094
}
10921095
```
@@ -1112,8 +1115,9 @@ It cannot be promisified using `util.promisify`. If needed, the `Promise`
11121115
constructor can be used:
11131116

11141117
```js
1118+
const store = new Map(); // initialize the store
11151119
new Promise((resolve, reject) => {
1116-
asyncLocalStorage.run(() => {
1120+
asyncLocalStorage.run(store, () => {
11171121
someFunction((err, result) => {
11181122
if (err) {
11191123
return reject(err);
@@ -1142,7 +1146,7 @@ the following pattern should be used:
11421146

11431147
```js
11441148
async function fn() {
1145-
await asyncLocalStorage.runSyncAndReturn(() => {
1149+
await asyncLocalStorage.runSyncAndReturn(new Map(), () => {
11461150
asyncLocalStorage.getStore().set('key', value);
11471151
return foo(); // The return value of foo will be awaited
11481152
});

lib/async_hooks.js

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
'use strict';
22

33
const {
4-
Map,
54
NumberIsSafeInteger,
65
ReflectApply,
76
Symbol,
8-
97
} = primordials;
108

119
const {
@@ -248,14 +246,14 @@ class AsyncLocalStorage {
248246
}
249247
}
250248

251-
_enter() {
249+
_enter(store) {
252250
if (!this.enabled) {
253251
this.enabled = true;
254252
storageList.push(this);
255253
storageHook.enable();
256254
}
257255
const resource = executionAsyncResource();
258-
resource[this.kResourceStore] = new Map();
256+
resource[this.kResourceStore] = store;
259257
}
260258

261259
_exit() {
@@ -265,8 +263,8 @@ class AsyncLocalStorage {
265263
}
266264
}
267265

268-
runSyncAndReturn(callback, ...args) {
269-
this._enter();
266+
runSyncAndReturn(store, callback, ...args) {
267+
this._enter(store);
270268
try {
271269
return callback(...args);
272270
} finally {
@@ -290,8 +288,8 @@ class AsyncLocalStorage {
290288
}
291289
}
292290

293-
run(callback, ...args) {
294-
this._enter();
291+
run(store, callback, ...args) {
292+
this._enter(store);
295293
process.nextTick(callback, ...args);
296294
this._exit();
297295
}

test/async-hooks/test-async-local-storage-args.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ const { AsyncLocalStorage } = require('async_hooks');
55

66
const asyncLocalStorage = new AsyncLocalStorage();
77

8-
asyncLocalStorage.run((runArg) => {
8+
asyncLocalStorage.run({}, (runArg) => {
99
assert.strictEqual(runArg, 1);
1010
asyncLocalStorage.exit((exitArg) => {
1111
assert.strictEqual(exitArg, 2);
1212
}, 2);
1313
}, 1);
1414

15-
asyncLocalStorage.runSyncAndReturn((runArg) => {
15+
asyncLocalStorage.runSyncAndReturn({}, (runArg) => {
1616
assert.strictEqual(runArg, 'foo');
1717
asyncLocalStorage.exitSyncAndReturn((exitArg) => {
1818
assert.strictEqual(exitArg, 'bar');

test/async-hooks/test-async-local-storage-async-await.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ async function test() {
1212
}
1313

1414
async function main() {
15-
await asyncLocalStorage.runSyncAndReturn(test);
15+
await asyncLocalStorage.runSyncAndReturn(new Map(), test);
1616
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
1717
}
1818

test/async-hooks/test-async-local-storage-async-functions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ async function testAwait() {
1919
await asyncLocalStorage.exitSyncAndReturn(testOut);
2020
}
2121

22-
asyncLocalStorage.run(() => {
22+
asyncLocalStorage.run(new Map(), () => {
2323
const store = asyncLocalStorage.getStore();
2424
store.set('key', 'value');
2525
testAwait(); // should not reject

test/async-hooks/test-async-local-storage-enable-disable.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ const { AsyncLocalStorage } = require('async_hooks');
55

66
const asyncLocalStorage = new AsyncLocalStorage();
77

8-
asyncLocalStorage.runSyncAndReturn(() => {
8+
asyncLocalStorage.runSyncAndReturn(new Map(), () => {
99
asyncLocalStorage.getStore().set('foo', 'bar');
1010
process.nextTick(() => {
1111
assert.strictEqual(asyncLocalStorage.getStore().get('foo'), 'bar');
1212
asyncLocalStorage.disable();
1313
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
1414
process.nextTick(() => {
1515
assert.strictEqual(asyncLocalStorage.getStore(), undefined);
16-
asyncLocalStorage.runSyncAndReturn(() => {
16+
asyncLocalStorage.runSyncAndReturn(new Map(), () => {
1717
assert.notStrictEqual(asyncLocalStorage.getStore(), undefined);
1818
});
1919
});

test/async-hooks/test-async-local-storage-errors-async.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ process.setUncaughtExceptionCaptureCallback((err) => {
1313
assert.strictEqual(asyncLocalStorage.getStore().get('hello'), 'node');
1414
});
1515

16-
asyncLocalStorage.run(() => {
16+
asyncLocalStorage.run(new Map(), () => {
1717
const store = asyncLocalStorage.getStore();
1818
store.set('hello', 'node');
1919
setTimeout(() => {

test/async-hooks/test-async-local-storage-errors-sync-ret.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ process.setUncaughtExceptionCaptureCallback((err) => {
1414
});
1515

1616
try {
17-
asyncLocalStorage.runSyncAndReturn(() => {
17+
asyncLocalStorage.runSyncAndReturn(new Map(), () => {
1818
const store = asyncLocalStorage.getStore();
1919
store.set('hello', 'node');
2020
setTimeout(() => {

test/async-hooks/test-async-local-storage-http.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const server = http.createServer((req, res) => {
1010
});
1111

1212
server.listen(0, () => {
13-
asyncLocalStorage.run(() => {
13+
asyncLocalStorage.run(new Map(), () => {
1414
const store = asyncLocalStorage.getStore();
1515
store.set('hello', 'world');
1616
http.get({ host: 'localhost', port: server.address().port }, () => {

0 commit comments

Comments
 (0)