Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvdeturckheim <vlad2t@hotmail.com>2021-03-09 17:06:20 +0300
committerMichael Dawson <mdawson@devrus.com>2021-06-04 21:03:43 +0300
commit7612d82c44c3a7f7e8bf58d57541606f3e7da37b (patch)
tree6eccb774975804a27c1b6a2226f10e2ae2891f9b /doc/api/async_hooks.md
parent6fe89a1fa6c10e1b68fe5c5d388a9d3e56bec138 (diff)
async_hooks: stabilize part of AsyncLocalStorage
Mark AsyncLocalStorage constructor, AsyncLocalStorage.prototype.getStore(), and AsyncLocalStorage.prototype.run as stable. PR-URL: https://github.com/nodejs/node/pull/37675 Reviewed-By: Bradley Farias <bradley.meck@gmail.com> Reviewed-By: Andrey Pechkurov <apechkurov@gmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de> Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Bryan English <bryan@bryanenglish.com>
Diffstat (limited to 'doc/api/async_hooks.md')
-rw-r--r--doc/api/async_hooks.md563
1 files changed, 4 insertions, 559 deletions
diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md
index 55cc0542561..f6805102fdf 100644
--- a/doc/api/async_hooks.md
+++ b/doc/api/async_hooks.md
@@ -664,575 +664,20 @@ like I/O, connection pooling, or managing callback queues may use the
### Class: `AsyncResource`
-The class `AsyncResource` is designed to be extended by the embedder's async
-resources. Using this, users can easily trigger the lifetime events of their
-own resources.
-
-The `init` hook will trigger when an `AsyncResource` is instantiated.
-
-The following is an overview of the `AsyncResource` API.
-
-```js
-const { AsyncResource, executionAsyncId } = require('async_hooks');
-
-// AsyncResource() is meant to be extended. Instantiating a
-// new AsyncResource() also triggers init. If triggerAsyncId is omitted then
-// async_hook.executionAsyncId() is used.
-const asyncResource = new AsyncResource(
- type, { triggerAsyncId: executionAsyncId(), requireManualDestroy: false }
-);
-
-// Run a function in the execution context of the resource. This will
-// * establish the context of the resource
-// * trigger the AsyncHooks before callbacks
-// * call the provided function `fn` with the supplied arguments
-// * trigger the AsyncHooks after callbacks
-// * restore the original execution context
-asyncResource.runInAsyncScope(fn, thisArg, ...args);
-
-// Call AsyncHooks destroy callbacks.
-asyncResource.emitDestroy();
-
-// Return the unique ID assigned to the AsyncResource instance.
-asyncResource.asyncId();
-
-// Return the trigger ID for the AsyncResource instance.
-asyncResource.triggerAsyncId();
-```
-
-#### `new AsyncResource(type[, options])`
-
-* `type` {string} The type of async event.
-* `options` {Object}
- * `triggerAsyncId` {number} The ID of the execution context that created this
- async event. **Default:** `executionAsyncId()`.
- * `requireManualDestroy` {boolean} If set to `true`, disables `emitDestroy`
- when the object is garbage collected. This usually does not need to be set
- (even if `emitDestroy` is called manually), unless the resource's `asyncId`
- is retrieved and the sensitive API's `emitDestroy` is called with it.
- When set to `false`, the `emitDestroy` call on garbage collection
- will only take place if there is at least one active `destroy` hook.
- **Default:** `false`.
-
-Example usage:
-
-```js
-class DBQuery extends AsyncResource {
- constructor(db) {
- super('DBQuery');
- this.db = db;
- }
-
- getInfo(query, callback) {
- this.db.get(query, (err, data) => {
- this.runInAsyncScope(callback, null, err, data);
- });
- }
-
- close() {
- this.db = null;
- this.emitDestroy();
- }
-}
-```
-
-#### Static method: `AsyncResource.bind(fn[, type, [thisArg]])`
-<!-- YAML
-added:
- - v14.8.0
- - v12.19.0
-changes:
- - version: v16.0.0
- pr-url: https://github.com/nodejs/node/pull/36782
- description: Added optional thisArg.
--->
-
-* `fn` {Function} The function to bind to the current execution context.
-* `type` {string} An optional name to associate with the underlying
- `AsyncResource`.
-* `thisArg` {any}
-
-Binds the given function to the current execution context.
-
-The returned function will have an `asyncResource` property referencing
-the `AsyncResource` to which the function is bound.
-
-#### `asyncResource.bind(fn[, thisArg])`
-<!-- YAML
-added:
- - v14.8.0
- - v12.19.0
-changes:
- - version: v16.0.0
- pr-url: https://github.com/nodejs/node/pull/36782
- description: Added optional thisArg.
--->
-
-* `fn` {Function} The function to bind to the current `AsyncResource`.
-* `thisArg` {any}
-
-Binds the given function to execute to this `AsyncResource`'s scope.
-
-The returned function will have an `asyncResource` property referencing
-the `AsyncResource` to which the function is bound.
-
-#### `asyncResource.runInAsyncScope(fn[, thisArg, ...args])`
-<!-- YAML
-added: v9.6.0
--->
-
-* `fn` {Function} The function to call in the execution context of this async
- resource.
-* `thisArg` {any} The receiver to be used for the function call.
-* `...args` {any} Optional arguments to pass to the function.
-
-Call the provided function with the provided arguments in the execution context
-of the async resource. This will establish the context, trigger the AsyncHooks
-before callbacks, call the function, trigger the AsyncHooks after callbacks, and
-then restore the original execution context.
-
-#### `asyncResource.emitDestroy()`
-
-* Returns: {AsyncResource} A reference to `asyncResource`.
-
-Call all `destroy` hooks. This should only ever be called once. An error will
-be thrown if it is called more than once. This **must** be manually called. If
-the resource is left to be collected by the GC then the `destroy` hooks will
-never be called.
-
-#### `asyncResource.asyncId()`
-
-* Returns: {number} The unique `asyncId` assigned to the resource.
-
-#### `asyncResource.triggerAsyncId()`
-
-* Returns: {number} The same `triggerAsyncId` that is passed to the
- `AsyncResource` constructor.
-
-<a id="async-resource-worker-pool"></a>
-### Using `AsyncResource` for a `Worker` thread pool
-
-The following example shows how to use the `AsyncResource` class to properly
-provide async tracking for a [`Worker`][] pool. Other resource pools, such as
-database connection pools, can follow a similar model.
-
-Assuming that the task is adding two numbers, using a file named
-`task_processor.js` with the following content:
-
-```js
-const { parentPort } = require('worker_threads');
-parentPort.on('message', (task) => {
- parentPort.postMessage(task.a + task.b);
-});
-```
-
-a Worker pool around it could use the following structure:
-
-```js
-const { AsyncResource } = require('async_hooks');
-const { EventEmitter } = require('events');
-const path = require('path');
-const { Worker } = require('worker_threads');
-
-const kTaskInfo = Symbol('kTaskInfo');
-const kWorkerFreedEvent = Symbol('kWorkerFreedEvent');
-
-class WorkerPoolTaskInfo extends AsyncResource {
- constructor(callback) {
- super('WorkerPoolTaskInfo');
- this.callback = callback;
- }
-
- done(err, result) {
- this.runInAsyncScope(this.callback, null, err, result);
- this.emitDestroy(); // `TaskInfo`s are used only once.
- }
-}
-
-class WorkerPool extends EventEmitter {
- constructor(numThreads) {
- super();
- this.numThreads = numThreads;
- this.workers = [];
- this.freeWorkers = [];
- this.tasks = [];
-
- for (let i = 0; i < numThreads; i++)
- this.addNewWorker();
-
- // Any time the kWorkerFreedEvent is emitted, dispatch
- // the next task pending in the queue, if any.
- this.on(kWorkerFreedEvent, () => {
- if (this.tasks.length > 0) {
- const { task, callback } = this.tasks.shift();
- this.runTask(task, callback);
- }
- });
- }
-
- addNewWorker() {
- const worker = new Worker(path.resolve(__dirname, 'task_processor.js'));
- worker.on('message', (result) => {
- // In case of success: Call the callback that was passed to `runTask`,
- // remove the `TaskInfo` associated with the Worker, and mark it as free
- // again.
- worker[kTaskInfo].done(null, result);
- worker[kTaskInfo] = null;
- this.freeWorkers.push(worker);
- this.emit(kWorkerFreedEvent);
- });
- worker.on('error', (err) => {
- // In case of an uncaught exception: Call the callback that was passed to
- // `runTask` with the error.
- if (worker[kTaskInfo])
- worker[kTaskInfo].done(err, null);
- else
- this.emit('error', err);
- // Remove the worker from the list and start a new Worker to replace the
- // current one.
- this.workers.splice(this.workers.indexOf(worker), 1);
- this.addNewWorker();
- });
- this.workers.push(worker);
- this.freeWorkers.push(worker);
- this.emit(kWorkerFreedEvent);
- }
-
- runTask(task, callback) {
- if (this.freeWorkers.length === 0) {
- // No free threads, wait until a worker thread becomes free.
- this.tasks.push({ task, callback });
- return;
- }
-
- const worker = this.freeWorkers.pop();
- worker[kTaskInfo] = new WorkerPoolTaskInfo(callback);
- worker.postMessage(task);
- }
-
- close() {
- for (const worker of this.workers) worker.terminate();
- }
-}
-
-module.exports = WorkerPool;
-```
-
-Without the explicit tracking added by the `WorkerPoolTaskInfo` objects,
-it would appear that the callbacks are associated with the individual `Worker`
-objects. However, the creation of the `Worker`s is not associated with the
-creation of the tasks and does not provide information about when tasks
-were scheduled.
-
-This pool could be used as follows:
-
-```js
-const WorkerPool = require('./worker_pool.js');
-const os = require('os');
-
-const pool = new WorkerPool(os.cpus().length);
-
-let finished = 0;
-for (let i = 0; i < 10; i++) {
- pool.runTask({ a: 42, b: 100 }, (err, result) => {
- console.log(i, err, result);
- if (++finished === 10)
- pool.close();
- });
-}
-```
-
-### Integrating `AsyncResource` with `EventEmitter`
-
-Event listeners triggered by an [`EventEmitter`][] may be run in a different
-execution context than the one that was active when `eventEmitter.on()` was
-called.
-
-The following example shows how to use the `AsyncResource` class to properly
-associate an event listener with the correct execution context. The same
-approach can be applied to a [`Stream`][] or a similar event-driven class.
-
-```js
-const { createServer } = require('http');
-const { AsyncResource, executionAsyncId } = require('async_hooks');
-
-const server = createServer((req, res) => {
- req.on('close', AsyncResource.bind(() => {
- // Execution context is bound to the current outer scope.
- }));
- req.on('close', () => {
- // Execution context is bound to the scope that caused 'close' to emit.
- });
- res.end();
-}).listen(3000);
-```
+The documentation for this class has moved [`AsyncResource`][].
## Class: `AsyncLocalStorage`
-<!-- YAML
-added:
- - v13.10.0
- - v12.17.0
--->
-
-This class is used to create asynchronous state within callbacks and promise
-chains. It allows storing data throughout the lifetime of a web request
-or any other asynchronous duration. It is similar to thread-local storage
-in other languages.
-
-While you can create your own implementation on top of the `async_hooks` module,
-`AsyncLocalStorage` should be preferred as it is a performant and memory safe
-implementation that involves significant optimizations that are non-obvious to
-implement.
-
-The following example uses `AsyncLocalStorage` to build a simple logger
-that assigns IDs to incoming HTTP requests and includes them in messages
-logged within each request.
-
-```js
-const http = require('http');
-const { AsyncLocalStorage } = require('async_hooks');
-
-const asyncLocalStorage = new AsyncLocalStorage();
-
-function logWithId(msg) {
- const id = asyncLocalStorage.getStore();
- console.log(`${id !== undefined ? id : '-'}:`, msg);
-}
-
-let idSeq = 0;
-http.createServer((req, res) => {
- asyncLocalStorage.run(idSeq++, () => {
- logWithId('start');
- // Imagine any chain of async operations here
- setImmediate(() => {
- logWithId('finish');
- res.end();
- });
- });
-}).listen(8080);
-
-http.get('http://localhost:8080');
-http.get('http://localhost:8080');
-// Prints:
-// 0: start
-// 1: start
-// 0: finish
-// 1: finish
-```
-
-When having multiple instances of `AsyncLocalStorage`, they are independent
-from each other. It is safe to instantiate this class multiple times.
-
-### `new AsyncLocalStorage()`
-<!-- YAML
-added:
- - v13.10.0
- - v12.17.0
--->
-
-Creates a new instance of `AsyncLocalStorage`. Store is only provided within a
-`run()` call or after an `enterWith()` call.
-
-### `asyncLocalStorage.disable()`
-<!-- YAML
-added:
- - v13.10.0
- - v12.17.0
--->
-
-Disables the instance of `AsyncLocalStorage`. All subsequent calls
-to `asyncLocalStorage.getStore()` will return `undefined` until
-`asyncLocalStorage.run()` or `asyncLocalStorage.enterWith()` is called again.
-
-When calling `asyncLocalStorage.disable()`, all current contexts linked to the
-instance will be exited.
-
-Calling `asyncLocalStorage.disable()` is required before the
-`asyncLocalStorage` can be garbage collected. This does not apply to stores
-provided by the `asyncLocalStorage`, as those objects are garbage collected
-along with the corresponding async resources.
-
-Use this method when the `asyncLocalStorage` is not in use anymore
-in the current process.
-
-### `asyncLocalStorage.getStore()`
-<!-- YAML
-added:
- - v13.10.0
- - v12.17.0
--->
-
-* Returns: {any}
-
-Returns the current store.
-If called outside of an asynchronous context initialized by
-calling `asyncLocalStorage.run()` or `asyncLocalStorage.enterWith()`, it
-returns `undefined`.
-
-### `asyncLocalStorage.enterWith(store)`
-<!-- YAML
-added:
- - v13.11.0
- - v12.17.0
--->
-
-* `store` {any}
-
-Transitions into the context for the remainder of the current
-synchronous execution and then persists the store through any following
-asynchronous calls.
-
-Example:
-
-```js
-const store = { id: 1 };
-// Replaces previous store with the given store object
-asyncLocalStorage.enterWith(store);
-asyncLocalStorage.getStore(); // Returns the store object
-someAsyncOperation(() => {
- asyncLocalStorage.getStore(); // Returns the same object
-});
-```
-
-This transition will continue for the _entire_ synchronous execution.
-This means that if, for example, the context is entered within an event
-handler subsequent event handlers will also run within that context unless
-specifically bound to another context with an `AsyncResource`. That is why
-`run()` should be preferred over `enterWith()` unless there are strong reasons
-to use the latter method.
-
-```js
-const store = { id: 1 };
-
-emitter.on('my-event', () => {
- asyncLocalStorage.enterWith(store);
-});
-emitter.on('my-event', () => {
- asyncLocalStorage.getStore(); // Returns the same object
-});
-
-asyncLocalStorage.getStore(); // Returns undefined
-emitter.emit('my-event');
-asyncLocalStorage.getStore(); // Returns the same object
-```
-
-### `asyncLocalStorage.run(store, callback[, ...args])`
-<!-- YAML
-added:
- - v13.10.0
- - v12.17.0
--->
-
-* `store` {any}
-* `callback` {Function}
-* `...args` {any}
-
-Runs a function synchronously within a context and returns its
-return value. The store is not accessible outside of the callback function.
-The store is accessible to any asynchronous operations created within the
-callback.
-
-The optional `args` are passed to the callback function.
-
-If the callback function throws an error, the error is thrown by `run()` too.
-The stacktrace is not impacted by this call and the context is exited.
-
-Example:
-
-```js
-const store = { id: 2 };
-try {
- asyncLocalStorage.run(store, () => {
- asyncLocalStorage.getStore(); // Returns the store object
- setTimeout(() => {
- asyncLocalStorage.getStore(); // Returns the store object
- }, 200);
- throw new Error();
- });
-} catch (e) {
- asyncLocalStorage.getStore(); // Returns undefined
- // The error will be caught here
-}
-```
-
-### `asyncLocalStorage.exit(callback[, ...args])`
-<!-- YAML
-added:
- - v13.10.0
- - v12.17.0
--->
-
-* `callback` {Function}
-* `...args` {any}
-
-Runs a function synchronously outside of a context and returns its
-return value. The store is not accessible within the callback function or
-the asynchronous operations created within the callback. Any `getStore()`
-call done within the callback function will always return `undefined`.
-
-The optional `args` are passed to the callback function.
-
-If the callback function throws an error, the error is thrown by `exit()` too.
-The stacktrace is not impacted by this call and the context is re-entered.
-
-Example:
-
-```js
-// Within a call to run
-try {
- asyncLocalStorage.getStore(); // Returns the store object or value
- asyncLocalStorage.exit(() => {
- asyncLocalStorage.getStore(); // Returns undefined
- throw new Error();
- });
-} catch (e) {
- asyncLocalStorage.getStore(); // Returns the same object or value
- // The error will be caught here
-}
-```
-
-### Usage with `async/await`
-
-If, within an async function, only one `await` call is to run within a context,
-the following pattern should be used:
-
-```js
-async function fn() {
- await asyncLocalStorage.run(new Map(), () => {
- asyncLocalStorage.getStore().set('key', value);
- return foo(); // The return value of foo will be awaited
- });
-}
-```
-
-In this example, the store is only available in the callback function and the
-functions called by `foo`. Outside of `run`, calling `getStore` will return
-`undefined`.
-
-### Troubleshooting
-
-In most cases your application or library code should have no issues with
-`AsyncLocalStorage`. But in rare cases you may face situations when the
-current store is lost in one of asynchronous operations. In those cases,
-consider the following options.
-
-If your code is callback-based, it is enough to promisify it with
-[`util.promisify()`][], so it starts working with native promises.
-If you need to keep using callback-based API, or your code assumes
-a custom thenable implementation, use the [`AsyncResource`][] class
-to associate the asynchronous operation with the correct execution context.
+The documentation for this class has moved [`AsyncLocalStorage`][].
[Hook Callbacks]: #async_hooks_hook_callbacks
[PromiseHooks]: https://docs.google.com/document/d/1rda3yKGHimKIhg5YeoAmCOtyURgsbTH_qaYR79FELlk/edit
-[`AsyncResource`]: #async_hooks_class_asyncresource
+[`AsyncLocalStorage`]: async_context.md#async_context_class_asynclocalstorage
+[`AsyncResource`]: async_context.md#async_context_class_asyncresource
[`after` callback]: #async_hooks_after_asyncid
[`before` callback]: #async_hooks_before_asyncid
[`destroy` callback]: #async_hooks_destroy_asyncid
[`init` callback]: #async_hooks_init_asyncid_type_triggerasyncid_resource
[`promiseResolve` callback]: #async_hooks_promiseresolve_asyncid
-[`EventEmitter`]: events.md#events_class_eventemitter
-[`Stream`]: stream.md#stream_stream
[`Worker`]: worker_threads.md#worker_threads_class_worker
-[`util.promisify()`]: util.md#util_util_promisify_original
[promise execution tracking]: #async_hooks_promise_execution_tracking