summaryrefslogtreecommitdiff
path: root/doc/api/async_hooks.md
diff options
context:
space:
mode:
authorvdeturckheim <vlad2t@hotmail.com>2019-03-01 21:31:36 +0100
committervdeturckheim <vlad2t@hotmail.com>2020-02-23 20:24:02 +0100
commit9c702922cdcf830cedb92d51e5dc9f956584c3ee (patch)
treeefec2c9529b6be48093462ca432fea33d0dbdf18 /doc/api/async_hooks.md
parent72b6cea25d4e43b2db63eeb32417088c5bd93307 (diff)
downloadnode-new-9c702922cdcf830cedb92d51e5dc9f956584c3ee.tar.gz
async_hooks: introduce async-context API
Adding AsyncLocalStorage class to async_hooks module. This API provide a simple CLS-like set of features. Co-authored-by: Andrey Pechkurov <apechkurov@gmail.com> PR-URL: https://github.com/nodejs/node/pull/26540 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michaƫl Zasso <targos@protonmail.com>
Diffstat (limited to 'doc/api/async_hooks.md')
-rw-r--r--doc/api/async_hooks.md287
1 files changed, 287 insertions, 0 deletions
diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md
index 70c31c8a42..529c6cc5a8 100644
--- a/doc/api/async_hooks.md
+++ b/doc/api/async_hooks.md
@@ -859,6 +859,293 @@ for (let i = 0; i < 10; i++) {
}
```
+## Class: `AsyncLocalStorage`
+<!-- YAML
+added: REPLACEME
+-->
+
+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.
+
+The following example builds a logger that will always know the current HTTP
+request and uses it to display enhanced logs without needing to explicitly
+provide the current HTTP request to it.
+
+```js
+const { AsyncLocalStorage } = require('async_hooks');
+const http = require('http');
+
+const kReq = 'CURRENT_REQUEST';
+const asyncLocalStorage = new AsyncLocalStorage();
+
+function log(...args) {
+ const store = asyncLocalStorage.getStore();
+ // Make sure the store exists and it contains a request.
+ if (store && store.has(kReq)) {
+ const req = store.get(kReq);
+ // Prints `GET /items ERR could not do something
+ console.log(req.method, req.url, ...args);
+ } else {
+ console.log(...args);
+ }
+}
+
+http.createServer((request, response) => {
+ asyncLocalStorage.run(() => {
+ const store = asyncLocalStorage.getStore();
+ store.set(kReq, request);
+ someAsyncOperation((err, result) => {
+ if (err) {
+ log('ERR', err.message);
+ }
+ });
+ });
+})
+.listen(8080);
+```
+
+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: REPLACEME
+-->
+
+Creates a new instance of `AsyncLocalStorage`. Store is only provided within a
+`run` or a `runSyncAndReturn` method call.
+
+### `asyncLocalStorage.disable()`
+<!-- YAML
+added: REPLACEME
+-->
+
+This method disables the instance of `AsyncLocalStorage`. All subsequent calls
+to `asyncLocalStorage.getStore()` will return `undefined` until
+`asyncLocalStorage.run()` or `asyncLocalStorage.runSyncAndReturn()`
+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.
+
+This method is to be used when the `asyncLocalStorage` is not in use anymore
+in the current process.
+
+### `asyncLocalStorage.getStore()`
+<!-- YAML
+added: REPLACEME
+-->
+
+* Returns: {Map}
+
+This method returns the current store.
+If this method is called outside of an asynchronous context initialized by
+calling `asyncLocalStorage.run` or `asyncLocalStorage.runAndReturn`, it will
+return `undefined`.
+
+### `asyncLocalStorage.run(callback[, ...args])`
+<!-- YAML
+added: REPLACEME
+-->
+
+* `callback` {Function}
+* `...args` {any}
+
+Calling `asyncLocalStorage.run(callback)` will create a new asynchronous
+context.
+Within the callback function and the asynchronous operations from the callback,
+`asyncLocalStorage.getStore()` will return an instance of `Map` known as
+"the store". This store will be persistent through the following
+asynchronous calls.
+
+The callback will be ran asynchronously. Optionally, arguments can be passed
+to the function. They will be passed to the callback function.
+
+If an error is thrown by the callback function, it will not be caught by
+a `try/catch` block as the callback is ran in a new asynchronous resource.
+Also, the stacktrace will be impacted by the asynchronous call.
+
+Example:
+
+```js
+asyncLocalStorage.run(() => {
+ asyncLocalStorage.getStore(); // Returns a Map
+ someAsyncOperation(() => {
+ asyncLocalStorage.getStore(); // Returns the same Map
+ });
+});
+asyncLocalStorage.getStore(); // Returns undefined
+```
+
+### `asyncLocalStorage.exit(callback[, ...args])`
+<!-- YAML
+added: REPLACEME
+-->
+
+* `callback` {Function}
+* `...args` {any}
+
+Calling `asyncLocalStorage.exit(callback)` will create a new asynchronous
+context.
+Within the callback function and the asynchronous operations from the callback,
+`asyncLocalStorage.getStore()` will return `undefined`.
+
+The callback will be ran asynchronously. Optionally, arguments can be passed
+to the function. They will be passed to the callback function.
+
+If an error is thrown by the callback function, it will not be caught by
+a `try/catch` block as the callback is ran in a new asynchronous resource.
+Also, the stacktrace will be impacted by the asynchronous call.
+
+Example:
+
+```js
+asyncLocalStorage.run(() => {
+ asyncLocalStorage.getStore(); // Returns a Map
+ asyncLocalStorage.exit(() => {
+ asyncLocalStorage.getStore(); // Returns undefined
+ });
+ asyncLocalStorage.getStore(); // Returns the same Map
+});
+```
+
+### `asyncLocalStorage.runSyncAndReturn(callback[, ...args])`
+<!-- YAML
+added: REPLACEME
+-->
+
+* `callback` {Function}
+* `...args` {any}
+
+This methods runs a function synchronously within a context and return its
+return value. The store is not accessible outside of the callback function or
+the asynchronous operations created within the callback.
+
+Optionally, arguments can be passed to the function. They will be passed to
+the callback function.
+
+If the callback function throws an error, it will be thrown by
+`runSyncAndReturn` too. The stacktrace will not be impacted by this call and
+the context will be exited.
+
+Example:
+
+```js
+try {
+ asyncLocalStorage.runSyncAndReturn(() => {
+ asyncLocalStorage.getStore(); // Returns a Map
+ throw new Error();
+ });
+} catch (e) {
+ asyncLocalStorage.getStore(); // Returns undefined
+ // The error will be caught here
+}
+```
+
+### `asyncLocalStorage.exitSyncAndReturn(callback[, ...args])`
+<!-- YAML
+added: REPLACEME
+-->
+
+* `callback` {Function}
+* `...args` {any}
+
+This methods runs a function synchronously outside of a context and return its
+return value. The store is not accessible within the callback function or
+the asynchronous operations created within the callback.
+
+Optionally, arguments can be passed to the function. They will be passed to
+the callback function.
+
+If the callback function throws an error, it will be thrown by
+`exitSyncAndReturn` too. The stacktrace will not be impacted by this call and
+the context will be re-entered.
+
+Example:
+
+```js
+// Within a call to run or runSyncAndReturn
+try {
+ asyncLocalStorage.getStore(); // Returns a Map
+ asyncLocalStorage.exitSyncAndReturn(() => {
+ asyncLocalStorage.getStore(); // Returns undefined
+ throw new Error();
+ });
+} catch (e) {
+ asyncLocalStorage.getStore(); // Returns the same Map
+ // The error will be caught here
+}
+```
+
+### Choosing between `run` and `runSyncAndReturn`
+
+#### When to choose `run`
+
+`run` is asynchronous. It is called with a callback function that
+runs within a new asynchronous call. This is the most explicit behavior as
+everything that is executed within the callback of `run` (including further
+asynchronous operations) will have access to the store.
+
+If an instance of `AsyncLocalStorage` is used for error management (for
+instance, with `process.setUncaughtExceptionCaptureCallback`), only
+exceptions thrown in the scope of the callback function will be associated
+with the context.
+
+This method is the safest as it provides strong scoping and consistent
+behavior.
+
+It cannot be promisified using `util.promisify`. If needed, the `Promise`
+constructor can be used:
+
+```js
+new Promise((resolve, reject) => {
+ asyncLocalStorage.run(() => {
+ someFunction((err, result) => {
+ if (err) {
+ return reject(err);
+ }
+ return resolve(result);
+ });
+ });
+});
+```
+
+#### When to choose `runSyncAndReturn`
+
+`runSyncAndReturn` is synchronous. The callback function will be executed
+synchronously and its return value will be returned by `runSyncAndReturn`.
+The store will only be accessible from within the callback
+function and the asynchronous operations created within this scope.
+If the callback throws an error, `runSyncAndReturn` will throw it and it will
+not be associated with the context.
+
+This method provides good scoping while being synchronous.
+
+#### 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.runSyncAndReturn(() => {
+ 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 `runSyncAndReturn`, calling `getStore`
+will return `undefined`.
+
[`after` callback]: #async_hooks_after_asyncid
[`before` callback]: #async_hooks_before_asyncid
[`destroy` callback]: #async_hooks_destroy_asyncid