• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Async hooks
2
3<!--introduced_in=v8.1.0-->
4
5> Stability: 1 - Experimental
6
7<!-- source_link=lib/async_hooks.js -->
8
9The `async_hooks` module provides an API to track asynchronous resources. It
10can be accessed using:
11
12```js
13const async_hooks = require('async_hooks');
14```
15
16## Terminology
17
18An asynchronous resource represents an object with an associated callback.
19This callback may be called multiple times, for example, the `'connection'`
20event in `net.createServer()`, or just a single time like in `fs.open()`.
21A resource can also be closed before the callback is called. `AsyncHook` does
22not explicitly distinguish between these different cases but will represent them
23as the abstract concept that is a resource.
24
25If [`Worker`][]s are used, each thread has an independent `async_hooks`
26interface, and each thread will use a new set of async IDs.
27
28## Overview
29
30Following is a simple overview of the public API.
31
32```js
33const async_hooks = require('async_hooks');
34
35// Return the ID of the current execution context.
36const eid = async_hooks.executionAsyncId();
37
38// Return the ID of the handle responsible for triggering the callback of the
39// current execution scope to call.
40const tid = async_hooks.triggerAsyncId();
41
42// Create a new AsyncHook instance. All of these callbacks are optional.
43const asyncHook =
44    async_hooks.createHook({ init, before, after, destroy, promiseResolve });
45
46// Allow callbacks of this AsyncHook instance to call. This is not an implicit
47// action after running the constructor, and must be explicitly run to begin
48// executing callbacks.
49asyncHook.enable();
50
51// Disable listening for new asynchronous events.
52asyncHook.disable();
53
54//
55// The following are the callbacks that can be passed to createHook().
56//
57
58// init is called during object construction. The resource may not have
59// completed construction when this callback runs, therefore all fields of the
60// resource referenced by "asyncId" may not have been populated.
61function init(asyncId, type, triggerAsyncId, resource) { }
62
63// Before is called just before the resource's callback is called. It can be
64// called 0-N times for handles (such as TCPWrap), and will be called exactly 1
65// time for requests (such as FSReqCallback).
66function before(asyncId) { }
67
68// After is called just after the resource's callback has finished.
69function after(asyncId) { }
70
71// Destroy is called when the resource is destroyed.
72function destroy(asyncId) { }
73
74// promiseResolve is called only for promise resources, when the
75// `resolve` function passed to the `Promise` constructor is invoked
76// (either directly or through other means of resolving a promise).
77function promiseResolve(asyncId) { }
78```
79
80## `async_hooks.createHook(callbacks)`
81
82<!-- YAML
83added: v8.1.0
84-->
85
86* `callbacks` {Object} The [Hook Callbacks][] to register
87  * `init` {Function} The [`init` callback][].
88  * `before` {Function} The [`before` callback][].
89  * `after` {Function} The [`after` callback][].
90  * `destroy` {Function} The [`destroy` callback][].
91  * `promiseResolve` {Function} The [`promiseResolve` callback][].
92* Returns: {AsyncHook} Instance used for disabling and enabling hooks
93
94Registers functions to be called for different lifetime events of each async
95operation.
96
97The callbacks `init()`/`before()`/`after()`/`destroy()` are called for the
98respective asynchronous event during a resource's lifetime.
99
100All callbacks are optional. For example, if only resource cleanup needs to
101be tracked, then only the `destroy` callback needs to be passed. The
102specifics of all functions that can be passed to `callbacks` is in the
103[Hook Callbacks][] section.
104
105```js
106const async_hooks = require('async_hooks');
107
108const asyncHook = async_hooks.createHook({
109  init(asyncId, type, triggerAsyncId, resource) { },
110  destroy(asyncId) { }
111});
112```
113
114The callbacks will be inherited via the prototype chain:
115
116```js
117class MyAsyncCallbacks {
118  init(asyncId, type, triggerAsyncId, resource) { }
119  destroy(asyncId) {}
120}
121
122class MyAddedCallbacks extends MyAsyncCallbacks {
123  before(asyncId) { }
124  after(asyncId) { }
125}
126
127const asyncHook = async_hooks.createHook(new MyAddedCallbacks());
128```
129
130Because promises are asynchronous resources whose lifecycle is tracked
131via the async hooks mechanism, the `init()`, `before()`, `after()`, and
132`destroy()` callbacks *must not* be async functions that return promises.
133
134### Error handling
135
136If any `AsyncHook` callbacks throw, the application will print the stack trace
137and exit. The exit path does follow that of an uncaught exception, but
138all `'uncaughtException'` listeners are removed, thus forcing the process to
139exit. The `'exit'` callbacks will still be called unless the application is run
140with `--abort-on-uncaught-exception`, in which case a stack trace will be
141printed and the application exits, leaving a core file.
142
143The reason for this error handling behavior is that these callbacks are running
144at potentially volatile points in an object's lifetime, for example during
145class construction and destruction. Because of this, it is deemed necessary to
146bring down the process quickly in order to prevent an unintentional abort in the
147future. This is subject to change in the future if a comprehensive analysis is
148performed to ensure an exception can follow the normal control flow without
149unintentional side effects.
150
151### Printing in AsyncHooks callbacks
152
153Because printing to the console is an asynchronous operation, `console.log()`
154will cause the AsyncHooks callbacks to be called. Using `console.log()` or
155similar asynchronous operations inside an AsyncHooks callback function will thus
156cause an infinite recursion. An easy solution to this when debugging is to use a
157synchronous logging operation such as `fs.writeFileSync(file, msg, flag)`.
158This will print to the file and will not invoke AsyncHooks recursively because
159it is synchronous.
160
161```js
162const fs = require('fs');
163const util = require('util');
164
165function debug(...args) {
166  // Use a function like this one when debugging inside an AsyncHooks callback
167  fs.writeFileSync('log.out', `${util.format(...args)}\n`, { flag: 'a' });
168}
169```
170
171If an asynchronous operation is needed for logging, it is possible to keep
172track of what caused the asynchronous operation using the information
173provided by AsyncHooks itself. The logging should then be skipped when
174it was the logging itself that caused AsyncHooks callback to call. By
175doing this the otherwise infinite recursion is broken.
176
177## Class: `AsyncHook`
178
179The class `AsyncHook` exposes an interface for tracking lifetime events
180of asynchronous operations.
181
182### `asyncHook.enable()`
183
184* Returns: {AsyncHook} A reference to `asyncHook`.
185
186Enable the callbacks for a given `AsyncHook` instance. If no callbacks are
187provided, enabling is a no-op.
188
189The `AsyncHook` instance is disabled by default. If the `AsyncHook` instance
190should be enabled immediately after creation, the following pattern can be used.
191
192```js
193const async_hooks = require('async_hooks');
194
195const hook = async_hooks.createHook(callbacks).enable();
196```
197
198### `asyncHook.disable()`
199
200* Returns: {AsyncHook} A reference to `asyncHook`.
201
202Disable the callbacks for a given `AsyncHook` instance from the global pool of
203`AsyncHook` callbacks to be executed. Once a hook has been disabled it will not
204be called again until enabled.
205
206For API consistency `disable()` also returns the `AsyncHook` instance.
207
208### Hook callbacks
209
210Key events in the lifetime of asynchronous events have been categorized into
211four areas: instantiation, before/after the callback is called, and when the
212instance is destroyed.
213
214#### `init(asyncId, type, triggerAsyncId, resource)`
215
216* `asyncId` {number} A unique ID for the async resource.
217* `type` {string} The type of the async resource.
218* `triggerAsyncId` {number} The unique ID of the async resource in whose
219  execution context this async resource was created.
220* `resource` {Object} Reference to the resource representing the async
221  operation, needs to be released during _destroy_.
222
223Called when a class is constructed that has the _possibility_ to emit an
224asynchronous event. This _does not_ mean the instance must call
225`before`/`after` before `destroy` is called, only that the possibility
226exists.
227
228This behavior can be observed by doing something like opening a resource then
229closing it before the resource can be used. The following snippet demonstrates
230this.
231
232```js
233require('net').createServer().listen(function() { this.close(); });
234// OR
235clearTimeout(setTimeout(() => {}, 10));
236```
237
238Every new resource is assigned an ID that is unique within the scope of the
239current Node.js instance.
240
241##### `type`
242
243The `type` is a string identifying the type of resource that caused
244`init` to be called. Generally, it will correspond to the name of the
245resource's constructor.
246
247```text
248FSEVENTWRAP, FSREQCALLBACK, GETADDRINFOREQWRAP, GETNAMEINFOREQWRAP, HTTPINCOMINGMESSAGE,
249HTTPCLIENTREQUEST, JSSTREAM, PIPECONNECTWRAP, PIPEWRAP, PROCESSWRAP, QUERYWRAP,
250SHUTDOWNWRAP, SIGNALWRAP, STATWATCHER, TCPCONNECTWRAP, TCPSERVERWRAP, TCPWRAP,
251TTYWRAP, UDPSENDWRAP, UDPWRAP, WRITEWRAP, ZLIB, SSLCONNECTION, PBKDF2REQUEST,
252RANDOMBYTESREQUEST, TLSWRAP, Microtask, Timeout, Immediate, TickObject
253```
254
255There is also the `PROMISE` resource type, which is used to track `Promise`
256instances and asynchronous work scheduled by them.
257
258Users are able to define their own `type` when using the public embedder API.
259
260It is possible to have type name collisions. Embedders are encouraged to use
261unique prefixes, such as the npm package name, to prevent collisions when
262listening to the hooks.
263
264##### `triggerAsyncId`
265
266`triggerAsyncId` is the `asyncId` of the resource that caused (or "triggered")
267the new resource to initialize and that caused `init` to call. This is different
268from `async_hooks.executionAsyncId()` that only shows *when* a resource was
269created, while `triggerAsyncId` shows *why* a resource was created.
270
271The following is a simple demonstration of `triggerAsyncId`:
272
273```js
274const { fd } = process.stdout;
275
276async_hooks.createHook({
277  init(asyncId, type, triggerAsyncId) {
278    const eid = async_hooks.executionAsyncId();
279    fs.writeSync(
280      fd,
281      `${type}(${asyncId}): trigger: ${triggerAsyncId} execution: ${eid}\n`);
282  }
283}).enable();
284
285net.createServer((conn) => {}).listen(8080);
286```
287
288Output when hitting the server with `nc localhost 8080`:
289
290```console
291TCPSERVERWRAP(5): trigger: 1 execution: 1
292TCPWRAP(7): trigger: 5 execution: 0
293```
294
295The `TCPSERVERWRAP` is the server which receives the connections.
296
297The `TCPWRAP` is the new connection from the client. When a new
298connection is made, the `TCPWrap` instance is immediately constructed. This
299happens outside of any JavaScript stack. (An `executionAsyncId()` of `0` means
300that it is being executed from C++ with no JavaScript stack above it.) With only
301that information, it would be impossible to link resources together in
302terms of what caused them to be created, so `triggerAsyncId` is given the task
303of propagating what resource is responsible for the new resource's existence.
304
305##### `resource`
306
307`resource` is an object that represents the actual async resource that has
308been initialized. This can contain useful information that can vary based on
309the value of `type`. For instance, for the `GETADDRINFOREQWRAP` resource type,
310`resource` provides the host name used when looking up the IP address for the
311host in `net.Server.listen()`. The API for accessing this information is
312not supported, but using the Embedder API, users can provide
313and document their own resource objects. For example, such a resource object
314could contain the SQL query being executed.
315
316In some cases the resource object is reused for performance reasons, it is
317thus not safe to use it as a key in a `WeakMap` or add properties to it.
318
319##### Asynchronous context example
320
321The following is an example with additional information about the calls to
322`init` between the `before` and `after` calls, specifically what the
323callback to `listen()` will look like. The output formatting is slightly more
324elaborate to make calling context easier to see.
325
326```js
327const { fd } = process.stdout;
328
329let indent = 0;
330async_hooks.createHook({
331  init(asyncId, type, triggerAsyncId) {
332    const eid = async_hooks.executionAsyncId();
333    const indentStr = ' '.repeat(indent);
334    fs.writeSync(
335      fd,
336      `${indentStr}${type}(${asyncId}):` +
337      ` trigger: ${triggerAsyncId} execution: ${eid}\n`);
338  },
339  before(asyncId) {
340    const indentStr = ' '.repeat(indent);
341    fs.writeSync(fd, `${indentStr}before:  ${asyncId}\n`);
342    indent += 2;
343  },
344  after(asyncId) {
345    indent -= 2;
346    const indentStr = ' '.repeat(indent);
347    fs.writeSync(fd, `${indentStr}after:  ${asyncId}\n`);
348  },
349  destroy(asyncId) {
350    const indentStr = ' '.repeat(indent);
351    fs.writeSync(fd, `${indentStr}destroy:  ${asyncId}\n`);
352  },
353}).enable();
354
355net.createServer(() => {}).listen(8080, () => {
356  // Let's wait 10ms before logging the server started.
357  setTimeout(() => {
358    console.log('>>>', async_hooks.executionAsyncId());
359  }, 10);
360});
361```
362
363Output from only starting the server:
364
365```console
366TCPSERVERWRAP(5): trigger: 1 execution: 1
367TickObject(6): trigger: 5 execution: 1
368before:  6
369  Timeout(7): trigger: 6 execution: 6
370after:   6
371destroy: 6
372before:  7
373>>> 7
374  TickObject(8): trigger: 7 execution: 7
375after:   7
376before:  8
377after:   8
378```
379
380As illustrated in the example, `executionAsyncId()` and `execution` each specify
381the value of the current execution context; which is delineated by calls to
382`before` and `after`.
383
384Only using `execution` to graph resource allocation results in the following:
385
386```console
387  root(1)
388     ^
389     |
390TickObject(6)
391     ^
392     |
393 Timeout(7)
394```
395
396The `TCPSERVERWRAP` is not part of this graph, even though it was the reason for
397`console.log()` being called. This is because binding to a port without a host
398name is a *synchronous* operation, but to maintain a completely asynchronous
399API the user's callback is placed in a `process.nextTick()`. Which is why
400`TickObject` is present in the output and is a 'parent' for `.listen()`
401callback.
402
403The graph only shows *when* a resource was created, not *why*, so to track
404the *why* use `triggerAsyncId`. Which can be represented with the following
405graph:
406
407```console
408 bootstrap(1)
409     |
410     ˅
411TCPSERVERWRAP(5)
412     |
413     ˅
414 TickObject(6)
415     |
416     ˅
417  Timeout(7)
418```
419
420#### `before(asyncId)`
421
422* `asyncId` {number}
423
424When an asynchronous operation is initiated (such as a TCP server receiving a
425new connection) or completes (such as writing data to disk) a callback is
426called to notify the user. The `before` callback is called just before said
427callback is executed. `asyncId` is the unique identifier assigned to the
428resource about to execute the callback.
429
430The `before` callback will be called 0 to N times. The `before` callback
431will typically be called 0 times if the asynchronous operation was cancelled
432or, for example, if no connections are received by a TCP server. Persistent
433asynchronous resources like a TCP server will typically call the `before`
434callback multiple times, while other operations like `fs.open()` will call
435it only once.
436
437#### `after(asyncId)`
438
439* `asyncId` {number}
440
441Called immediately after the callback specified in `before` is completed.
442
443If an uncaught exception occurs during execution of the callback, then `after`
444will run *after* the `'uncaughtException'` event is emitted or a `domain`'s
445handler runs.
446
447#### `destroy(asyncId)`
448
449* `asyncId` {number}
450
451Called after the resource corresponding to `asyncId` is destroyed. It is also
452called asynchronously from the embedder API `emitDestroy()`.
453
454Some resources depend on garbage collection for cleanup, so if a reference is
455made to the `resource` object passed to `init` it is possible that `destroy`
456will never be called, causing a memory leak in the application. If the resource
457does not depend on garbage collection, then this will not be an issue.
458
459#### `promiseResolve(asyncId)`
460
461<!-- YAML
462added: v8.6.0
463-->
464
465* `asyncId` {number}
466
467Called when the `resolve` function passed to the `Promise` constructor is
468invoked (either directly or through other means of resolving a promise).
469
470`resolve()` does not do any observable synchronous work.
471
472The `Promise` is not necessarily fulfilled or rejected at this point if the
473`Promise` was resolved by assuming the state of another `Promise`.
474
475```js
476new Promise((resolve) => resolve(true)).then((a) => {});
477```
478
479calls the following callbacks:
480
481```text
482init for PROMISE with id 5, trigger id: 1
483  promise resolve 5      # corresponds to resolve(true)
484init for PROMISE with id 6, trigger id: 5  # the Promise returned by then()
485  before 6               # the then() callback is entered
486  promise resolve 6      # the then() callback resolves the promise by returning
487  after 6
488```
489
490### `async_hooks.executionAsyncResource()`
491
492<!-- YAML
493added: v13.9.0
494-->
495
496* Returns: {Object} The resource representing the current execution.
497  Useful to store data within the resource.
498
499Resource objects returned by `executionAsyncResource()` are most often internal
500Node.js handle objects with undocumented APIs. Using any functions or properties
501on the object is likely to crash your application and should be avoided.
502
503Using `executionAsyncResource()` in the top-level execution context will
504return an empty object as there is no handle or request object to use,
505but having an object representing the top-level can be helpful.
506
507```js
508const { open } = require('fs');
509const { executionAsyncId, executionAsyncResource } = require('async_hooks');
510
511console.log(executionAsyncId(), executionAsyncResource());  // 1 {}
512open(__filename, 'r', (err, fd) => {
513  console.log(executionAsyncId(), executionAsyncResource());  // 7 FSReqWrap
514});
515```
516
517This can be used to implement continuation local storage without the
518use of a tracking `Map` to store the metadata:
519
520```js
521const { createServer } = require('http');
522const {
523  executionAsyncId,
524  executionAsyncResource,
525  createHook
526} = require('async_hooks');
527const sym = Symbol('state'); // Private symbol to avoid pollution
528
529createHook({
530  init(asyncId, type, triggerAsyncId, resource) {
531    const cr = executionAsyncResource();
532    if (cr) {
533      resource[sym] = cr[sym];
534    }
535  }
536}).enable();
537
538const server = createServer((req, res) => {
539  executionAsyncResource()[sym] = { state: req.url };
540  setTimeout(function() {
541    res.end(JSON.stringify(executionAsyncResource()[sym]));
542  }, 100);
543}).listen(3000);
544```
545
546### `async_hooks.executionAsyncId()`
547
548<!-- YAML
549added: v8.1.0
550changes:
551  - version: v8.2.0
552    pr-url: https://github.com/nodejs/node/pull/13490
553    description: Renamed from `currentId`.
554-->
555
556* Returns: {number} The `asyncId` of the current execution context. Useful to
557  track when something calls.
558
559```js
560const async_hooks = require('async_hooks');
561
562console.log(async_hooks.executionAsyncId());  // 1 - bootstrap
563fs.open(path, 'r', (err, fd) => {
564  console.log(async_hooks.executionAsyncId());  // 6 - open()
565});
566```
567
568The ID returned from `executionAsyncId()` is related to execution timing, not
569causality (which is covered by `triggerAsyncId()`):
570
571```js
572const server = net.createServer((conn) => {
573  // Returns the ID of the server, not of the new connection, because the
574  // callback runs in the execution scope of the server's MakeCallback().
575  async_hooks.executionAsyncId();
576
577}).listen(port, () => {
578  // Returns the ID of a TickObject (process.nextTick()) because all
579  // callbacks passed to .listen() are wrapped in a nextTick().
580  async_hooks.executionAsyncId();
581});
582```
583
584Promise contexts may not get precise `executionAsyncIds` by default.
585See the section on [promise execution tracking][].
586
587### `async_hooks.triggerAsyncId()`
588
589* Returns: {number} The ID of the resource responsible for calling the callback
590  that is currently being executed.
591
592```js
593const server = net.createServer((conn) => {
594  // The resource that caused (or triggered) this callback to be called
595  // was that of the new connection. Thus the return value of triggerAsyncId()
596  // is the asyncId of "conn".
597  async_hooks.triggerAsyncId();
598
599}).listen(port, () => {
600  // Even though all callbacks passed to .listen() are wrapped in a nextTick()
601  // the callback itself exists because the call to the server's .listen()
602  // was made. So the return value would be the ID of the server.
603  async_hooks.triggerAsyncId();
604});
605```
606
607Promise contexts may not get valid `triggerAsyncId`s by default. See
608the section on [promise execution tracking][].
609
610## Promise execution tracking
611
612By default, promise executions are not assigned `asyncId`s due to the relatively
613expensive nature of the [promise introspection API][PromiseHooks] provided by
614V8. This means that programs using promises or `async`/`await` will not get
615correct execution and trigger ids for promise callback contexts by default.
616
617```js
618const ah = require('async_hooks');
619Promise.resolve(1729).then(() => {
620  console.log(`eid ${ah.executionAsyncId()} tid ${ah.triggerAsyncId()}`);
621});
622// produces:
623// eid 1 tid 0
624```
625
626Observe that the `then()` callback claims to have executed in the context of the
627outer scope even though there was an asynchronous hop involved. Also,
628the `triggerAsyncId` value is `0`, which means that we are missing context about
629the resource that caused (triggered) the `then()` callback to be executed.
630
631Installing async hooks via `async_hooks.createHook` enables promise execution
632tracking:
633
634```js
635const ah = require('async_hooks');
636ah.createHook({ init() {} }).enable(); // forces PromiseHooks to be enabled.
637Promise.resolve(1729).then(() => {
638  console.log(`eid ${ah.executionAsyncId()} tid ${ah.triggerAsyncId()}`);
639});
640// produces:
641// eid 7 tid 6
642```
643
644In this example, adding any actual hook function enabled the tracking of
645promises. There are two promises in the example above; the promise created by
646`Promise.resolve()` and the promise returned by the call to `then()`. In the
647example above, the first promise got the `asyncId` `6` and the latter got
648`asyncId` `7`. During the execution of the `then()` callback, we are executing
649in the context of promise with `asyncId` `7`. This promise was triggered by
650async resource `6`.
651
652Another subtlety with promises is that `before` and `after` callbacks are run
653only on chained promises. That means promises not created by `then()`/`catch()`
654will not have the `before` and `after` callbacks fired on them. For more details
655see the details of the V8 [PromiseHooks][] API.
656
657## JavaScript embedder API
658
659Library developers that handle their own asynchronous resources performing tasks
660like I/O, connection pooling, or managing callback queues may use the
661`AsyncResource` JavaScript API so that all the appropriate callbacks are called.
662
663### Class: `AsyncResource`
664
665The class `AsyncResource` is designed to be extended by the embedder's async
666resources. Using this, users can easily trigger the lifetime events of their
667own resources.
668
669The `init` hook will trigger when an `AsyncResource` is instantiated.
670
671The following is an overview of the `AsyncResource` API.
672
673```js
674const { AsyncResource, executionAsyncId } = require('async_hooks');
675
676// AsyncResource() is meant to be extended. Instantiating a
677// new AsyncResource() also triggers init. If triggerAsyncId is omitted then
678// async_hook.executionAsyncId() is used.
679const asyncResource = new AsyncResource(
680  type, { triggerAsyncId: executionAsyncId(), requireManualDestroy: false }
681);
682
683// Run a function in the execution context of the resource. This will
684// * establish the context of the resource
685// * trigger the AsyncHooks before callbacks
686// * call the provided function `fn` with the supplied arguments
687// * trigger the AsyncHooks after callbacks
688// * restore the original execution context
689asyncResource.runInAsyncScope(fn, thisArg, ...args);
690
691// Call AsyncHooks destroy callbacks.
692asyncResource.emitDestroy();
693
694// Return the unique ID assigned to the AsyncResource instance.
695asyncResource.asyncId();
696
697// Return the trigger ID for the AsyncResource instance.
698asyncResource.triggerAsyncId();
699```
700
701#### `new AsyncResource(type[, options])`
702
703* `type` {string} The type of async event.
704* `options` {Object}
705  * `triggerAsyncId` {number} The ID of the execution context that created this
706    async event. **Default:** `executionAsyncId()`.
707  * `requireManualDestroy` {boolean} If set to `true`, disables `emitDestroy`
708    when the object is garbage collected. This usually does not need to be set
709    (even if `emitDestroy` is called manually), unless the resource's `asyncId`
710    is retrieved and the sensitive API's `emitDestroy` is called with it.
711    When set to `false`, the `emitDestroy` call on garbage collection
712    will only take place if there is at least one active `destroy` hook.
713    **Default:** `false`.
714
715Example usage:
716
717```js
718class DBQuery extends AsyncResource {
719  constructor(db) {
720    super('DBQuery');
721    this.db = db;
722  }
723
724  getInfo(query, callback) {
725    this.db.get(query, (err, data) => {
726      this.runInAsyncScope(callback, null, err, data);
727    });
728  }
729
730  close() {
731    this.db = null;
732    this.emitDestroy();
733  }
734}
735```
736
737#### Static method: `AsyncResource.bind(fn[, type])`
738<!-- YAML
739added: v14.8.0
740-->
741
742* `fn` {Function} The function to bind to the current execution context.
743* `type` {string} An optional name to associate with the underlying
744  `AsyncResource`.
745
746Binds the given function to the current execution context.
747
748The returned function will have an `asyncResource` property referencing
749the `AsyncResource` to which the function is bound.
750
751#### `asyncResource.bind(fn)`
752<!-- YAML
753added: v14.8.0
754-->
755
756* `fn` {Function} The function to bind to the current `AsyncResource`.
757
758Binds the given function to execute to this `AsyncResource`'s scope.
759
760The returned function will have an `asyncResource` property referencing
761the `AsyncResource` to which the function is bound.
762
763#### `asyncResource.runInAsyncScope(fn[, thisArg, ...args])`
764<!-- YAML
765added: v9.6.0
766-->
767
768* `fn` {Function} The function to call in the execution context of this async
769  resource.
770* `thisArg` {any} The receiver to be used for the function call.
771* `...args` {any} Optional arguments to pass to the function.
772
773Call the provided function with the provided arguments in the execution context
774of the async resource. This will establish the context, trigger the AsyncHooks
775before callbacks, call the function, trigger the AsyncHooks after callbacks, and
776then restore the original execution context.
777
778#### `asyncResource.emitDestroy()`
779
780* Returns: {AsyncResource} A reference to `asyncResource`.
781
782Call all `destroy` hooks. This should only ever be called once. An error will
783be thrown if it is called more than once. This **must** be manually called. If
784the resource is left to be collected by the GC then the `destroy` hooks will
785never be called.
786
787#### `asyncResource.asyncId()`
788
789* Returns: {number} The unique `asyncId` assigned to the resource.
790
791#### `asyncResource.triggerAsyncId()`
792
793* Returns: {number} The same `triggerAsyncId` that is passed to the
794  `AsyncResource` constructor.
795
796<a id="async-resource-worker-pool"></a>
797### Using `AsyncResource` for a `Worker` thread pool
798
799The following example shows how to use the `AsyncResource` class to properly
800provide async tracking for a [`Worker`][] pool. Other resource pools, such as
801database connection pools, can follow a similar model.
802
803Assuming that the task is adding two numbers, using a file named
804`task_processor.js` with the following content:
805
806```js
807const { parentPort } = require('worker_threads');
808parentPort.on('message', (task) => {
809  parentPort.postMessage(task.a + task.b);
810});
811```
812
813a Worker pool around it could use the following structure:
814
815```js
816const { AsyncResource } = require('async_hooks');
817const { EventEmitter } = require('events');
818const path = require('path');
819const { Worker } = require('worker_threads');
820
821const kTaskInfo = Symbol('kTaskInfo');
822const kWorkerFreedEvent = Symbol('kWorkerFreedEvent');
823
824class WorkerPoolTaskInfo extends AsyncResource {
825  constructor(callback) {
826    super('WorkerPoolTaskInfo');
827    this.callback = callback;
828  }
829
830  done(err, result) {
831    this.runInAsyncScope(this.callback, null, err, result);
832    this.emitDestroy();  // `TaskInfo`s are used only once.
833  }
834}
835
836class WorkerPool extends EventEmitter {
837  constructor(numThreads) {
838    super();
839    this.numThreads = numThreads;
840    this.workers = [];
841    this.freeWorkers = [];
842    this.tasks = [];
843
844    for (let i = 0; i < numThreads; i++)
845      this.addNewWorker();
846
847    // Any time the kWorkerFreedEvent is emitted, dispatch
848    // the next task pending in the queue, if any.
849    this.on(kWorkerFreedEvent, () => {
850      if (this.tasks.length > 0) {
851        const { task, callback } = this.tasks.shift();
852        this.runTask(task, callback);
853      }
854    });
855  }
856
857  addNewWorker() {
858    const worker = new Worker(path.resolve(__dirname, 'task_processor.js'));
859    worker.on('message', (result) => {
860      // In case of success: Call the callback that was passed to `runTask`,
861      // remove the `TaskInfo` associated with the Worker, and mark it as free
862      // again.
863      worker[kTaskInfo].done(null, result);
864      worker[kTaskInfo] = null;
865      this.freeWorkers.push(worker);
866      this.emit(kWorkerFreedEvent);
867    });
868    worker.on('error', (err) => {
869      // In case of an uncaught exception: Call the callback that was passed to
870      // `runTask` with the error.
871      if (worker[kTaskInfo])
872        worker[kTaskInfo].done(err, null);
873      else
874        this.emit('error', err);
875      // Remove the worker from the list and start a new Worker to replace the
876      // current one.
877      this.workers.splice(this.workers.indexOf(worker), 1);
878      this.addNewWorker();
879    });
880    this.workers.push(worker);
881    this.freeWorkers.push(worker);
882    this.emit(kWorkerFreedEvent);
883  }
884
885  runTask(task, callback) {
886    if (this.freeWorkers.length === 0) {
887      // No free threads, wait until a worker thread becomes free.
888      this.tasks.push({ task, callback });
889      return;
890    }
891
892    const worker = this.freeWorkers.pop();
893    worker[kTaskInfo] = new WorkerPoolTaskInfo(callback);
894    worker.postMessage(task);
895  }
896
897  close() {
898    for (const worker of this.workers) worker.terminate();
899  }
900}
901
902module.exports = WorkerPool;
903```
904
905Without the explicit tracking added by the `WorkerPoolTaskInfo` objects,
906it would appear that the callbacks are associated with the individual `Worker`
907objects. However, the creation of the `Worker`s is not associated with the
908creation of the tasks and does not provide information about when tasks
909were scheduled.
910
911This pool could be used as follows:
912
913```js
914const WorkerPool = require('./worker_pool.js');
915const os = require('os');
916
917const pool = new WorkerPool(os.cpus().length);
918
919let finished = 0;
920for (let i = 0; i < 10; i++) {
921  pool.runTask({ a: 42, b: 100 }, (err, result) => {
922    console.log(i, err, result);
923    if (++finished === 10)
924      pool.close();
925  });
926}
927```
928
929### Integrating `AsyncResource` with `EventEmitter`
930
931Event listeners triggered by an [`EventEmitter`][] may be run in a different
932execution context than the one that was active when `eventEmitter.on()` was
933called.
934
935The following example shows how to use the `AsyncResource` class to properly
936associate an event listener with the correct execution context. The same
937approach can be applied to a [`Stream`][] or a similar event-driven class.
938
939```js
940const { createServer } = require('http');
941const { AsyncResource, executionAsyncId } = require('async_hooks');
942
943const server = createServer((req, res) => {
944  req.on('close', AsyncResource.bind(() => {
945    // Execution context is bound to the current outer scope.
946  }));
947  req.on('close', () => {
948    // Execution context is bound to the scope that caused 'close' to emit.
949  });
950  res.end();
951}).listen(3000);
952```
953
954## Class: `AsyncLocalStorage`
955<!-- YAML
956added: v13.10.0
957-->
958
959This class is used to create asynchronous state within callbacks and promise
960chains. It allows storing data throughout the lifetime of a web request
961or any other asynchronous duration. It is similar to thread-local storage
962in other languages.
963
964While you can create your own implementation on top of the `async_hooks` module,
965`AsyncLocalStorage` should be preferred as it is a performant and memory safe
966implementation that involves significant optimizations that are non-obvious to
967implement.
968
969The following example uses `AsyncLocalStorage` to build a simple logger
970that assigns IDs to incoming HTTP requests and includes them in messages
971logged within each request.
972
973```js
974const http = require('http');
975const { AsyncLocalStorage } = require('async_hooks');
976
977const asyncLocalStorage = new AsyncLocalStorage();
978
979function logWithId(msg) {
980  const id = asyncLocalStorage.getStore();
981  console.log(`${id !== undefined ? id : '-'}:`, msg);
982}
983
984let idSeq = 0;
985http.createServer((req, res) => {
986  asyncLocalStorage.run(idSeq++, () => {
987    logWithId('start');
988    // Imagine any chain of async operations here
989    setImmediate(() => {
990      logWithId('finish');
991      res.end();
992    });
993  });
994}).listen(8080);
995
996http.get('http://localhost:8080');
997http.get('http://localhost:8080');
998// Prints:
999//   0: start
1000//   1: start
1001//   0: finish
1002//   1: finish
1003```
1004
1005When having multiple instances of `AsyncLocalStorage`, they are independent
1006from each other. It is safe to instantiate this class multiple times.
1007
1008### `new AsyncLocalStorage()`
1009<!-- YAML
1010added: v13.10.0
1011-->
1012
1013Creates a new instance of `AsyncLocalStorage`. Store is only provided within a
1014`run()` call or after an `enterWith()` call.
1015
1016### `asyncLocalStorage.disable()`
1017<!-- YAML
1018added: v13.10.0
1019-->
1020
1021Disables the instance of `AsyncLocalStorage`. All subsequent calls
1022to `asyncLocalStorage.getStore()` will return `undefined` until
1023`asyncLocalStorage.run()` or `asyncLocalStorage.enterWith()` is called again.
1024
1025When calling `asyncLocalStorage.disable()`, all current contexts linked to the
1026instance will be exited.
1027
1028Calling `asyncLocalStorage.disable()` is required before the
1029`asyncLocalStorage` can be garbage collected. This does not apply to stores
1030provided by the `asyncLocalStorage`, as those objects are garbage collected
1031along with the corresponding async resources.
1032
1033Use this method when the `asyncLocalStorage` is not in use anymore
1034in the current process.
1035
1036### `asyncLocalStorage.getStore()`
1037<!-- YAML
1038added: v13.10.0
1039-->
1040
1041* Returns: {any}
1042
1043Returns the current store.
1044If called outside of an asynchronous context initialized by
1045calling `asyncLocalStorage.run()` or `asyncLocalStorage.enterWith()`, it
1046returns `undefined`.
1047
1048### `asyncLocalStorage.enterWith(store)`
1049<!-- YAML
1050added: v13.11.0
1051-->
1052
1053* `store` {any}
1054
1055Transitions into the context for the remainder of the current
1056synchronous execution and then persists the store through any following
1057asynchronous calls.
1058
1059Example:
1060
1061```js
1062const store = { id: 1 };
1063// Replaces previous store with the given store object
1064asyncLocalStorage.enterWith(store);
1065asyncLocalStorage.getStore(); // Returns the store object
1066someAsyncOperation(() => {
1067  asyncLocalStorage.getStore(); // Returns the same object
1068});
1069```
1070
1071This transition will continue for the _entire_ synchronous execution.
1072This means that if, for example, the context is entered within an event
1073handler subsequent event handlers will also run within that context unless
1074specifically bound to another context with an `AsyncResource`. That is why
1075`run()` should be preferred over `enterWith()` unless there are strong reasons
1076to use the latter method.
1077
1078```js
1079const store = { id: 1 };
1080
1081emitter.on('my-event', () => {
1082  asyncLocalStorage.enterWith(store);
1083});
1084emitter.on('my-event', () => {
1085  asyncLocalStorage.getStore(); // Returns the same object
1086});
1087
1088asyncLocalStorage.getStore(); // Returns undefined
1089emitter.emit('my-event');
1090asyncLocalStorage.getStore(); // Returns the same object
1091```
1092
1093### `asyncLocalStorage.run(store, callback[, ...args])`
1094<!-- YAML
1095added: v13.10.0
1096-->
1097
1098* `store` {any}
1099* `callback` {Function}
1100* `...args` {any}
1101
1102Runs a function synchronously within a context and returns its
1103return value. The store is not accessible outside of the callback function.
1104The store is accessible to any asynchronous operations created within the
1105callback.
1106
1107The optional `args` are passed to the callback function.
1108
1109If the callback function throws an error, the error is thrown by `run()` too.
1110The stacktrace is not impacted by this call and the context is exited.
1111
1112Example:
1113
1114```js
1115const store = { id: 2 };
1116try {
1117  asyncLocalStorage.run(store, () => {
1118    asyncLocalStorage.getStore(); // Returns the store object
1119    setTimeout(() => {
1120      asyncLocalStorage.getStore(); // Returns the store object
1121    }, 200);
1122    throw new Error();
1123  });
1124} catch (e) {
1125  asyncLocalStorage.getStore(); // Returns undefined
1126  // The error will be caught here
1127}
1128```
1129
1130### `asyncLocalStorage.exit(callback[, ...args])`
1131<!-- YAML
1132added: v13.10.0
1133-->
1134
1135* `callback` {Function}
1136* `...args` {any}
1137
1138Runs a function synchronously outside of a context and returns its
1139return value. The store is not accessible within the callback function or
1140the asynchronous operations created within the callback. Any `getStore()`
1141call done within the callback function will always return `undefined`.
1142
1143The optional `args` are passed to the callback function.
1144
1145If the callback function throws an error, the error is thrown by `exit()` too.
1146The stacktrace is not impacted by this call and the context is re-entered.
1147
1148Example:
1149
1150```js
1151// Within a call to run
1152try {
1153  asyncLocalStorage.getStore(); // Returns the store object or value
1154  asyncLocalStorage.exit(() => {
1155    asyncLocalStorage.getStore(); // Returns undefined
1156    throw new Error();
1157  });
1158} catch (e) {
1159  asyncLocalStorage.getStore(); // Returns the same object or value
1160  // The error will be caught here
1161}
1162```
1163
1164### Usage with `async/await`
1165
1166If, within an async function, only one `await` call is to run within a context,
1167the following pattern should be used:
1168
1169```js
1170async function fn() {
1171  await asyncLocalStorage.run(new Map(), () => {
1172    asyncLocalStorage.getStore().set('key', value);
1173    return foo(); // The return value of foo will be awaited
1174  });
1175}
1176```
1177
1178In this example, the store is only available in the callback function and the
1179functions called by `foo`. Outside of `run`, calling `getStore` will return
1180`undefined`.
1181
1182### Troubleshooting
1183
1184In most cases your application or library code should have no issues with
1185`AsyncLocalStorage`. But in rare cases you may face situations when the
1186current store is lost in one of asynchronous operations. In those cases,
1187consider the following options.
1188
1189If your code is callback-based, it is enough to promisify it with
1190[`util.promisify()`][], so it starts working with native promises.
1191
1192If you need to keep using callback-based API, or your code assumes
1193a custom thenable implementation, use the [`AsyncResource`][] class
1194to associate the asynchronous operation with the correct execution context.
1195
1196[Hook Callbacks]: #async_hooks_hook_callbacks
1197[PromiseHooks]: https://docs.google.com/document/d/1rda3yKGHimKIhg5YeoAmCOtyURgsbTH_qaYR79FELlk/edit
1198[`AsyncResource`]: #async_hooks_class_asyncresource
1199[`EventEmitter`]: events.md#events_class_eventemitter
1200[`Stream`]: stream.md#stream_stream
1201[`Worker`]: worker_threads.md#worker_threads_class_worker
1202[`after` callback]: #async_hooks_after_asyncid
1203[`before` callback]: #async_hooks_before_asyncid
1204[`destroy` callback]: #async_hooks_destroy_asyncid
1205[`init` callback]: #async_hooks_init_asyncid_type_triggerasyncid_resource
1206[`promiseResolve` callback]: #async_hooks_promiseresolve_asyncid
1207[`util.promisify()`]: util.md#util_util_promisify_original
1208[promise execution tracking]: #async_hooks_promise_execution_tracking
1209