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