Skip to content
Open
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
164 changes: 164 additions & 0 deletions doc/api/async_context.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,110 @@ try {
}
```

### `asyncLocalStorage.withScope(store)`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
> Stability: 1 - Experimental
> Stability: 1 - Experimental

Perhaps 1.1 Active Development instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The particular change is inherited from #61674. Do you want that made there? What about the new APIs in diagnostics_channel? Same status?


* `store` {any}
* Returns: {RunScope}

Creates a disposable scope that enters the given store and automatically
restores the previous store value when the scope is disposed. This method is
designed to work with JavaScript's explicit resource management (`using` syntax).

Example:

```mjs
import { AsyncLocalStorage } from 'node:async_hooks';

const asyncLocalStorage = new AsyncLocalStorage();

{
using _ = asyncLocalStorage.withScope('my-store');
console.log(asyncLocalStorage.getStore()); // Prints: my-store
}

console.log(asyncLocalStorage.getStore()); // Prints: undefined
```

```cjs
const { AsyncLocalStorage } = require('node:async_hooks');

const asyncLocalStorage = new AsyncLocalStorage();

{
using _ = asyncLocalStorage.withScope('my-store');
console.log(asyncLocalStorage.getStore()); // Prints: my-store
}

console.log(asyncLocalStorage.getStore()); // Prints: undefined
```

The `withScope()` method is particularly useful for managing context in
synchronous code where you want to ensure the previous store value is restored
when exiting a block, even if an error is thrown.

```mjs
import { AsyncLocalStorage } from 'node:async_hooks';

const asyncLocalStorage = new AsyncLocalStorage();

try {
using _ = asyncLocalStorage.withScope('my-store');
console.log(asyncLocalStorage.getStore()); // Prints: my-store
throw new Error('test');
} catch (e) {
// Store is automatically restored even after error
console.log(asyncLocalStorage.getStore()); // Prints: undefined
}
```

```cjs
const { AsyncLocalStorage } = require('node:async_hooks');

const asyncLocalStorage = new AsyncLocalStorage();

try {
using _ = asyncLocalStorage.withScope('my-store');
console.log(asyncLocalStorage.getStore()); // Prints: my-store
throw new Error('test');
} catch (e) {
// Store is automatically restored even after error
console.log(asyncLocalStorage.getStore()); // Prints: undefined
}
```

**Important:** When using `withScope()` in async functions before the first
`await`, be aware that the scope change will affect the caller's context. The
synchronous portion of an async function (before the first `await`) runs
immediately when called, and when it reaches the first `await`, it returns the
promise to the caller. At that point, the scope change becomes visible in the
caller's context and will persist in subsequent synchronous code until something
else changes the scope value. For async operations, prefer using `run()` which
properly isolates context across async boundaries.

```mjs
import { AsyncLocalStorage } from 'node:async_hooks';

const asyncLocalStorage = new AsyncLocalStorage();

async function example() {
using _ = asyncLocalStorage.withScope('my-store');
console.log(asyncLocalStorage.getStore()); // Prints: my-store
await someAsyncOperation(); // Function pauses here and returns promise
console.log(asyncLocalStorage.getStore()); // Prints: my-store
}

// Calling without await
example(); // Synchronous portion runs, then pauses at first await
// After the promise is returned, the scope 'my-store' is now active in caller!
console.log(asyncLocalStorage.getStore()); // Prints: my-store (unexpected!)
```

### Usage with `async/await`

If, within an async function, only one `await` call is to run within a context,
Expand Down Expand Up @@ -420,6 +524,64 @@ of `asyncLocalStorage.getStore()` after the calls you suspect are responsible
for the loss. When the code logs `undefined`, the last callback called is
probably responsible for the context loss.

## Class: `RunScope`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

A disposable scope returned by [`asyncLocalStorage.withScope()`][] that
automatically restores the previous store value when disposed. This class
implements the [Explicit Resource Management][] protocol and is designed to work
with JavaScript's `using` syntax.

The scope automatically restores the previous store value when the `using` block
exits, whether through normal completion or by throwing an error.

### `scope.dispose()`

<!-- YAML
added: REPLACEME
-->

Explicitly ends the scope and restores the previous store value. This method
is idempotent: calling it multiple times has the same effect as calling it once.

The `[Symbol.dispose]()` method defers to `dispose()`.

If `withScope()` is called without the `using` keyword, `dispose()` must be
called manually to restore the previous store value. Forgetting to call
`dispose()` will cause the store value to persist for the remainder of the
current execution context:

```mjs
import { AsyncLocalStorage } from 'node:async_hooks';

const storage = new AsyncLocalStorage();

// Without using, the scope must be disposed manually
const scope = storage.withScope('my-store');
// storage.getStore() === 'my-store' here

scope.dispose(); // Restore previous value
// storage.getStore() === undefined here
```

```cjs
const { AsyncLocalStorage } = require('node:async_hooks');

const storage = new AsyncLocalStorage();

// Without using, the scope must be disposed manually
const scope = storage.withScope('my-store');
// storage.getStore() === 'my-store' here

scope.dispose(); // Restore previous value
// storage.getStore() === undefined here
```

## Class: `AsyncResource`

<!-- YAML
Expand Down Expand Up @@ -905,8 +1067,10 @@ const server = createServer((req, res) => {
}).listen(3000);
```

[Explicit Resource Management]: https://github.com/tc39/proposal-explicit-resource-management
[`AsyncResource`]: #class-asyncresource
[`EventEmitter`]: events.md#class-eventemitter
[`Stream`]: stream.md#stream
[`Worker`]: worker_threads.md#class-worker
[`asyncLocalStorage.withScope()`]: #asynclocalstoragewithscopestore
[`util.promisify()`]: util.md#utilpromisifyoriginal
Loading
Loading