Skip to content

Commit f1b7982

Browse files
flakey5danielleadams
authored andcommitted
lib: add AsyncLocalStorage.bind() and .snapshot()
PR-URL: #46387 Reviewed-By: Stephen Belanger <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]> Reviewed-By: Gerhard Stöbich <[email protected]>
1 parent 247caac commit f1b7982

4 files changed

+91
-0
lines changed

doc/api/async_context.md

+50
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,56 @@ changes:
134134
Creates a new instance of `AsyncLocalStorage`. Store is only provided within a
135135
`run()` call or after an `enterWith()` call.
136136

137+
### Static method: `AsyncLocalStorage.bind(fn)`
138+
139+
<!-- YAML
140+
added: REPLACEME
141+
-->
142+
143+
> Stability: 1 - Experimental
144+
145+
* `fn` {Function} The function to bind to the current execution context.
146+
* Returns: {Function} A new function that calls `fn` within the captured
147+
execution context.
148+
149+
Binds the given function to the current execution context.
150+
151+
### Static method: `AsyncLocalStorage.snapshot()`
152+
153+
<!-- YAML
154+
added: REPLACEME
155+
-->
156+
157+
> Stability: 1 - Experimental
158+
159+
* Returns: {Function} A new function with the signature
160+
`(fn: (...args) : R, ...args) : R`.
161+
162+
Captures the current execution context and returns a function that accepts a
163+
function as an argument. Whenever the returned function is called, it
164+
calls the function passed to it within the captured context.
165+
166+
```js
167+
const asyncLocalStorage = new AsyncLocalStorage();
168+
const runInAsyncScope = asyncLocalStorage.run(123, () => asyncLocalStorage.snapshot());
169+
const result = asyncLocalStorage.run(321, () => runInAsyncScope(() => asyncLocalStorage.getStore()));
170+
console.log(result); // returns 123
171+
```
172+
173+
AsyncLocalStorage.snapshot() can replace the use of AsyncResource for simple
174+
async context tracking purposes, for example:
175+
176+
```js
177+
class Foo {
178+
#runInAsyncScope = AsyncLocalStorage.snapshot();
179+
180+
get() { return this.#runInAsyncScope(() => asyncLocalStorage.getStore()); }
181+
}
182+
183+
const foo = asyncLocalStorage.run(123, () => new Foo());
184+
console.log(asyncLocalStorage.run(321, () => foo.get())); // returns 123
185+
```
186+
137187
### `asyncLocalStorage.disable()`
138188

139189
<!-- YAML

lib/async_hooks.js

+8
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,14 @@ class AsyncLocalStorage {
279279
this.enabled = false;
280280
}
281281

282+
static bind(fn) {
283+
return AsyncResource.bind(fn);
284+
}
285+
286+
static snapshot() {
287+
return AsyncLocalStorage.bind((cb, ...args) => cb(...args));
288+
}
289+
282290
disable() {
283291
if (this.enabled) {
284292
this.enabled = false;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const { AsyncLocalStorage } = require('async_hooks');
6+
7+
[1, false, '', {}, []].forEach((i) => {
8+
assert.throws(() => AsyncLocalStorage.bind(i), {
9+
code: 'ERR_INVALID_ARG_TYPE'
10+
});
11+
});
12+
13+
const fn = common.mustCall(AsyncLocalStorage.bind(() => 123));
14+
assert.strictEqual(fn(), 123);
15+
16+
const fn2 = AsyncLocalStorage.bind(common.mustCall((arg) => assert.strictEqual(arg, 'test')));
17+
fn2('test');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const { strictEqual } = require('assert');
5+
const { AsyncLocalStorage } = require('async_hooks');
6+
7+
const asyncLocalStorage = new AsyncLocalStorage();
8+
const runInAsyncScope =
9+
asyncLocalStorage.run(123, common.mustCall(() => AsyncLocalStorage.snapshot()));
10+
const result =
11+
asyncLocalStorage.run(321, common.mustCall(() => {
12+
return runInAsyncScope(() => {
13+
return asyncLocalStorage.getStore();
14+
});
15+
}));
16+
strictEqual(result, 123);

0 commit comments

Comments
 (0)