Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

async_hooks: add AsyncLocalStorage.bind() and AsyncLocalStorage.snapshot() #46387

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
flakey5 marked this conversation as resolved.
Show resolved Hide resolved
* Returns: {Function} A new function that calls `fn` within the captured
execution context.

Binds the given function to the current execution context.
jasnell marked this conversation as resolved.
Show resolved Hide resolved

### Static method: `AsyncLocalStorage.snapshot()`
jasnell marked this conversation as resolved.
Show resolved Hide resolved

<!-- 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);