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