Skip to content

Commit

Permalink
lib: add AsyncLocalStorage.bind() and .snapshot()
Browse files Browse the repository at this point in the history
PR-URL: #46387
Reviewed-By: Stephen Belanger <[email protected]>
Reviewed-By: Chengzhong Wu <[email protected]>
Reviewed-By: Gerhard Stöbich <[email protected]>
  • Loading branch information
flakey5 authored and jasnell committed Feb 19, 2023
1 parent 225c578 commit 9a82938
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 0 deletions.
50 changes: 50 additions & 0 deletions doc/api/async_context.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,56 @@ changes:
Creates a new instance of `AsyncLocalStorage`. Store is only provided within a
`run()` call or after an `enterWith()` call.

### Static method: `AsyncLocalStorage.bind(fn)`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental
* `fn` {Function} The function to bind to the current execution context.
* Returns: {Function} A new function that calls `fn` within the captured
execution context.

Binds the given function to the current execution context.

### Static method: `AsyncLocalStorage.snapshot()`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental
* Returns: {Function} A new function with the signature
`(fn: (...args) : R, ...args) : R`.

Captures the current execution context and returns a function that accepts a
function as an argument. Whenever the returned function is called, it
calls the function passed to it within the captured context.

```js
const asyncLocalStorage = new AsyncLocalStorage();
const runInAsyncScope = asyncLocalStorage.run(123, () => asyncLocalStorage.snapshot());
const result = asyncLocalStorage.run(321, () => runInAsyncScope(() => asyncLocalStorage.getStore()));
console.log(result); // returns 123
```

AsyncLocalStorage.snapshot() can replace the use of AsyncResource for simple
async context tracking purposes, for example:

```js
class Foo {
#runInAsyncScope = AsyncLocalStorage.snapshot();

get() { return this.#runInAsyncScope(() => asyncLocalStorage.getStore()); }
}

const foo = asyncLocalStorage.run(123, () => new Foo());
console.log(asyncLocalStorage.run(321, () => foo.get())); // returns 123
```

### `asyncLocalStorage.disable()`

<!-- YAML
Expand Down
8 changes: 8 additions & 0 deletions lib/async_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,14 @@ class AsyncLocalStorage {
this.enabled = false;
}

static bind(fn) {
return AsyncResource.bind(fn);
}

static snapshot() {
return AsyncLocalStorage.bind((cb, ...args) => cb(...args));
}

disable() {
if (this.enabled) {
this.enabled = false;
Expand Down
17 changes: 17 additions & 0 deletions test/parallel/test-async-local-storage-bind.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';

const common = require('../common');
const assert = require('assert');
const { AsyncLocalStorage } = require('async_hooks');

[1, false, '', {}, []].forEach((i) => {
assert.throws(() => AsyncLocalStorage.bind(i), {
code: 'ERR_INVALID_ARG_TYPE'
});
});

const fn = common.mustCall(AsyncLocalStorage.bind(() => 123));
assert.strictEqual(fn(), 123);

const fn2 = AsyncLocalStorage.bind(common.mustCall((arg) => assert.strictEqual(arg, 'test')));
fn2('test');
16 changes: 16 additions & 0 deletions test/parallel/test-async-local-storage-snapshot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use strict';

const common = require('../common');
const { strictEqual } = require('assert');
const { AsyncLocalStorage } = require('async_hooks');

const asyncLocalStorage = new AsyncLocalStorage();
const runInAsyncScope =
asyncLocalStorage.run(123, common.mustCall(() => AsyncLocalStorage.snapshot()));
const result =
asyncLocalStorage.run(321, common.mustCall(() => {
return runInAsyncScope(() => {
return asyncLocalStorage.getStore();
});
}));
strictEqual(result, 123);

0 comments on commit 9a82938

Please sign in to comment.