1# Async hooks 2 3<!--introduced_in=v8.1.0--> 4 5> Stability: 1 - Experimental. Please migrate away from this API, if you can. 6> We do not recommend using the [`createHook`][], [`AsyncHook`][], and 7> [`executionAsyncResource`][] APIs as they have usability issues, safety risks, 8> and performance implications. Async context tracking use cases are better 9> served by the stable [`AsyncLocalStorage`][] API. If you have a use case for 10> `createHook`, `AsyncHook`, or `executionAsyncResource` beyond the context 11> tracking need solved by [`AsyncLocalStorage`][] or diagnostics data currently 12> provided by [Diagnostics Channel][], please open an issue at 13> <https://github.com/nodejs/node/issues> describing your use case so we can 14> create a more purpose-focused API. 15 16<!-- source_link=lib/async_hooks.js --> 17 18We strongly discourage the use of the `async_hooks` API. 19Other APIs that can cover most of its use cases include: 20 21* [`AsyncLocalStorage`][] tracks async context 22* [`process.getActiveResourcesInfo()`][] tracks active resources 23 24The `node:async_hooks` module provides an API to track asynchronous resources. 25It can be accessed using: 26 27```mjs 28import async_hooks from 'node:async_hooks'; 29``` 30 31```cjs 32const async_hooks = require('node:async_hooks'); 33``` 34 35## Terminology 36 37An asynchronous resource represents an object with an associated callback. 38This callback may be called multiple times, such as the `'connection'` 39event in `net.createServer()`, or just a single time like in `fs.open()`. 40A resource can also be closed before the callback is called. `AsyncHook` does 41not explicitly distinguish between these different cases but will represent them 42as the abstract concept that is a resource. 43 44If [`Worker`][]s are used, each thread has an independent `async_hooks` 45interface, and each thread will use a new set of async IDs. 46 47## Overview 48 49Following is a simple overview of the public API. 50 51```mjs 52import async_hooks from 'node:async_hooks'; 53 54// Return the ID of the current execution context. 55const eid = async_hooks.executionAsyncId(); 56 57// Return the ID of the handle responsible for triggering the callback of the 58// current execution scope to call. 59const tid = async_hooks.triggerAsyncId(); 60 61// Create a new AsyncHook instance. All of these callbacks are optional. 62const asyncHook = 63 async_hooks.createHook({ init, before, after, destroy, promiseResolve }); 64 65// Allow callbacks of this AsyncHook instance to call. This is not an implicit 66// action after running the constructor, and must be explicitly run to begin 67// executing callbacks. 68asyncHook.enable(); 69 70// Disable listening for new asynchronous events. 71asyncHook.disable(); 72 73// 74// The following are the callbacks that can be passed to createHook(). 75// 76 77// init() is called during object construction. The resource may not have 78// completed construction when this callback runs. Therefore, all fields of the 79// resource referenced by "asyncId" may not have been populated. 80function init(asyncId, type, triggerAsyncId, resource) { } 81 82// before() is called just before the resource's callback is called. It can be 83// called 0-N times for handles (such as TCPWrap), and will be called exactly 1 84// time for requests (such as FSReqCallback). 85function before(asyncId) { } 86 87// after() is called just after the resource's callback has finished. 88function after(asyncId) { } 89 90// destroy() is called when the resource is destroyed. 91function destroy(asyncId) { } 92 93// promiseResolve() is called only for promise resources, when the 94// resolve() function passed to the Promise constructor is invoked 95// (either directly or through other means of resolving a promise). 96function promiseResolve(asyncId) { } 97``` 98 99```cjs 100const async_hooks = require('node:async_hooks'); 101 102// Return the ID of the current execution context. 103const eid = async_hooks.executionAsyncId(); 104 105// Return the ID of the handle responsible for triggering the callback of the 106// current execution scope to call. 107const tid = async_hooks.triggerAsyncId(); 108 109// Create a new AsyncHook instance. All of these callbacks are optional. 110const asyncHook = 111 async_hooks.createHook({ init, before, after, destroy, promiseResolve }); 112 113// Allow callbacks of this AsyncHook instance to call. This is not an implicit 114// action after running the constructor, and must be explicitly run to begin 115// executing callbacks. 116asyncHook.enable(); 117 118// Disable listening for new asynchronous events. 119asyncHook.disable(); 120 121// 122// The following are the callbacks that can be passed to createHook(). 123// 124 125// init() is called during object construction. The resource may not have 126// completed construction when this callback runs. Therefore, all fields of the 127// resource referenced by "asyncId" may not have been populated. 128function init(asyncId, type, triggerAsyncId, resource) { } 129 130// before() is called just before the resource's callback is called. It can be 131// called 0-N times for handles (such as TCPWrap), and will be called exactly 1 132// time for requests (such as FSReqCallback). 133function before(asyncId) { } 134 135// after() is called just after the resource's callback has finished. 136function after(asyncId) { } 137 138// destroy() is called when the resource is destroyed. 139function destroy(asyncId) { } 140 141// promiseResolve() is called only for promise resources, when the 142// resolve() function passed to the Promise constructor is invoked 143// (either directly or through other means of resolving a promise). 144function promiseResolve(asyncId) { } 145``` 146 147## `async_hooks.createHook(callbacks)` 148 149<!-- YAML 150added: v8.1.0 151--> 152 153* `callbacks` {Object} The [Hook Callbacks][] to register 154 * `init` {Function} The [`init` callback][]. 155 * `before` {Function} The [`before` callback][]. 156 * `after` {Function} The [`after` callback][]. 157 * `destroy` {Function} The [`destroy` callback][]. 158 * `promiseResolve` {Function} The [`promiseResolve` callback][]. 159* Returns: {AsyncHook} Instance used for disabling and enabling hooks 160 161Registers functions to be called for different lifetime events of each async 162operation. 163 164The callbacks `init()`/`before()`/`after()`/`destroy()` are called for the 165respective asynchronous event during a resource's lifetime. 166 167All callbacks are optional. For example, if only resource cleanup needs to 168be tracked, then only the `destroy` callback needs to be passed. The 169specifics of all functions that can be passed to `callbacks` is in the 170[Hook Callbacks][] section. 171 172```mjs 173import { createHook } from 'node:async_hooks'; 174 175const asyncHook = createHook({ 176 init(asyncId, type, triggerAsyncId, resource) { }, 177 destroy(asyncId) { }, 178}); 179``` 180 181```cjs 182const async_hooks = require('node:async_hooks'); 183 184const asyncHook = async_hooks.createHook({ 185 init(asyncId, type, triggerAsyncId, resource) { }, 186 destroy(asyncId) { }, 187}); 188``` 189 190The callbacks will be inherited via the prototype chain: 191 192```js 193class MyAsyncCallbacks { 194 init(asyncId, type, triggerAsyncId, resource) { } 195 destroy(asyncId) {} 196} 197 198class MyAddedCallbacks extends MyAsyncCallbacks { 199 before(asyncId) { } 200 after(asyncId) { } 201} 202 203const asyncHook = async_hooks.createHook(new MyAddedCallbacks()); 204``` 205 206Because promises are asynchronous resources whose lifecycle is tracked 207via the async hooks mechanism, the `init()`, `before()`, `after()`, and 208`destroy()` callbacks _must not_ be async functions that return promises. 209 210### Error handling 211 212If any `AsyncHook` callbacks throw, the application will print the stack trace 213and exit. The exit path does follow that of an uncaught exception, but 214all `'uncaughtException'` listeners are removed, thus forcing the process to 215exit. The `'exit'` callbacks will still be called unless the application is run 216with `--abort-on-uncaught-exception`, in which case a stack trace will be 217printed and the application exits, leaving a core file. 218 219The reason for this error handling behavior is that these callbacks are running 220at potentially volatile points in an object's lifetime, for example during 221class construction and destruction. Because of this, it is deemed necessary to 222bring down the process quickly in order to prevent an unintentional abort in the 223future. This is subject to change in the future if a comprehensive analysis is 224performed to ensure an exception can follow the normal control flow without 225unintentional side effects. 226 227### Printing in `AsyncHook` callbacks 228 229Because printing to the console is an asynchronous operation, `console.log()` 230will cause `AsyncHook` callbacks to be called. Using `console.log()` or 231similar asynchronous operations inside an `AsyncHook` callback function will 232cause an infinite recursion. An easy solution to this when debugging is to use a 233synchronous logging operation such as `fs.writeFileSync(file, msg, flag)`. 234This will print to the file and will not invoke `AsyncHook` recursively because 235it is synchronous. 236 237```mjs 238import { writeFileSync } from 'node:fs'; 239import { format } from 'node:util'; 240 241function debug(...args) { 242 // Use a function like this one when debugging inside an AsyncHook callback 243 writeFileSync('log.out', `${format(...args)}\n`, { flag: 'a' }); 244} 245``` 246 247```cjs 248const fs = require('node:fs'); 249const util = require('node:util'); 250 251function debug(...args) { 252 // Use a function like this one when debugging inside an AsyncHook callback 253 fs.writeFileSync('log.out', `${util.format(...args)}\n`, { flag: 'a' }); 254} 255``` 256 257If an asynchronous operation is needed for logging, it is possible to keep 258track of what caused the asynchronous operation using the information 259provided by `AsyncHook` itself. The logging should then be skipped when 260it was the logging itself that caused the `AsyncHook` callback to be called. By 261doing this, the otherwise infinite recursion is broken. 262 263## Class: `AsyncHook` 264 265The class `AsyncHook` exposes an interface for tracking lifetime events 266of asynchronous operations. 267 268### `asyncHook.enable()` 269 270* Returns: {AsyncHook} A reference to `asyncHook`. 271 272Enable the callbacks for a given `AsyncHook` instance. If no callbacks are 273provided, enabling is a no-op. 274 275The `AsyncHook` instance is disabled by default. If the `AsyncHook` instance 276should be enabled immediately after creation, the following pattern can be used. 277 278```mjs 279import { createHook } from 'node:async_hooks'; 280 281const hook = createHook(callbacks).enable(); 282``` 283 284```cjs 285const async_hooks = require('node:async_hooks'); 286 287const hook = async_hooks.createHook(callbacks).enable(); 288``` 289 290### `asyncHook.disable()` 291 292* Returns: {AsyncHook} A reference to `asyncHook`. 293 294Disable the callbacks for a given `AsyncHook` instance from the global pool of 295`AsyncHook` callbacks to be executed. Once a hook has been disabled it will not 296be called again until enabled. 297 298For API consistency `disable()` also returns the `AsyncHook` instance. 299 300### Hook callbacks 301 302Key events in the lifetime of asynchronous events have been categorized into 303four areas: instantiation, before/after the callback is called, and when the 304instance is destroyed. 305 306#### `init(asyncId, type, triggerAsyncId, resource)` 307 308* `asyncId` {number} A unique ID for the async resource. 309* `type` {string} The type of the async resource. 310* `triggerAsyncId` {number} The unique ID of the async resource in whose 311 execution context this async resource was created. 312* `resource` {Object} Reference to the resource representing the async 313 operation, needs to be released during _destroy_. 314 315Called when a class is constructed that has the _possibility_ to emit an 316asynchronous event. This _does not_ mean the instance must call 317`before`/`after` before `destroy` is called, only that the possibility 318exists. 319 320This behavior can be observed by doing something like opening a resource then 321closing it before the resource can be used. The following snippet demonstrates 322this. 323 324```mjs 325import { createServer } from 'node:net'; 326 327createServer().listen(function() { this.close(); }); 328// OR 329clearTimeout(setTimeout(() => {}, 10)); 330``` 331 332```cjs 333require('node:net').createServer().listen(function() { this.close(); }); 334// OR 335clearTimeout(setTimeout(() => {}, 10)); 336``` 337 338Every new resource is assigned an ID that is unique within the scope of the 339current Node.js instance. 340 341##### `type` 342 343The `type` is a string identifying the type of resource that caused 344`init` to be called. Generally, it will correspond to the name of the 345resource's constructor. 346 347The `type` of resources created by Node.js itself can change in any Node.js 348release. Valid values include `TLSWRAP`, 349`TCPWRAP`, `TCPSERVERWRAP`, `GETADDRINFOREQWRAP`, `FSREQCALLBACK`, 350`Microtask`, and `Timeout`. Inspect the source code of the Node.js version used 351to get the full list. 352 353Furthermore users of [`AsyncResource`][] create async resources independent 354of Node.js itself. 355 356There is also the `PROMISE` resource type, which is used to track `Promise` 357instances and asynchronous work scheduled by them. 358 359Users are able to define their own `type` when using the public embedder API. 360 361It is possible to have type name collisions. Embedders are encouraged to use 362unique prefixes, such as the npm package name, to prevent collisions when 363listening to the hooks. 364 365##### `triggerAsyncId` 366 367`triggerAsyncId` is the `asyncId` of the resource that caused (or "triggered") 368the new resource to initialize and that caused `init` to call. This is different 369from `async_hooks.executionAsyncId()` that only shows _when_ a resource was 370created, while `triggerAsyncId` shows _why_ a resource was created. 371 372The following is a simple demonstration of `triggerAsyncId`: 373 374```mjs 375import { createHook, executionAsyncId } from 'node:async_hooks'; 376import { stdout } from 'node:process'; 377import net from 'node:net'; 378import fs from 'node:fs'; 379 380createHook({ 381 init(asyncId, type, triggerAsyncId) { 382 const eid = executionAsyncId(); 383 fs.writeSync( 384 stdout.fd, 385 `${type}(${asyncId}): trigger: ${triggerAsyncId} execution: ${eid}\n`); 386 }, 387}).enable(); 388 389net.createServer((conn) => {}).listen(8080); 390``` 391 392```cjs 393const { createHook, executionAsyncId } = require('node:async_hooks'); 394const { stdout } = require('node:process'); 395const net = require('node:net'); 396const fs = require('node:fs'); 397 398createHook({ 399 init(asyncId, type, triggerAsyncId) { 400 const eid = executionAsyncId(); 401 fs.writeSync( 402 stdout.fd, 403 `${type}(${asyncId}): trigger: ${triggerAsyncId} execution: ${eid}\n`); 404 }, 405}).enable(); 406 407net.createServer((conn) => {}).listen(8080); 408``` 409 410Output when hitting the server with `nc localhost 8080`: 411 412```console 413TCPSERVERWRAP(5): trigger: 1 execution: 1 414TCPWRAP(7): trigger: 5 execution: 0 415``` 416 417The `TCPSERVERWRAP` is the server which receives the connections. 418 419The `TCPWRAP` is the new connection from the client. When a new 420connection is made, the `TCPWrap` instance is immediately constructed. This 421happens outside of any JavaScript stack. (An `executionAsyncId()` of `0` means 422that it is being executed from C++ with no JavaScript stack above it.) With only 423that information, it would be impossible to link resources together in 424terms of what caused them to be created, so `triggerAsyncId` is given the task 425of propagating what resource is responsible for the new resource's existence. 426 427##### `resource` 428 429`resource` is an object that represents the actual async resource that has 430been initialized. The API to access the object may be specified by the 431creator of the resource. Resources created by Node.js itself are internal 432and may change at any time. Therefore no API is specified for these. 433 434In some cases the resource object is reused for performance reasons, it is 435thus not safe to use it as a key in a `WeakMap` or add properties to it. 436 437##### Asynchronous context example 438 439The context tracking use case is covered by the stable API [`AsyncLocalStorage`][]. 440This example only illustrates async hooks operation but [`AsyncLocalStorage`][] 441fits better to this use case. 442 443The following is an example with additional information about the calls to 444`init` between the `before` and `after` calls, specifically what the 445callback to `listen()` will look like. The output formatting is slightly more 446elaborate to make calling context easier to see. 447 448```mjs 449import async_hooks from 'node:async_hooks'; 450import fs from 'node:fs'; 451import net from 'node:net'; 452import { stdout } from 'node:process'; 453const { fd } = stdout; 454 455let indent = 0; 456async_hooks.createHook({ 457 init(asyncId, type, triggerAsyncId) { 458 const eid = async_hooks.executionAsyncId(); 459 const indentStr = ' '.repeat(indent); 460 fs.writeSync( 461 fd, 462 `${indentStr}${type}(${asyncId}):` + 463 ` trigger: ${triggerAsyncId} execution: ${eid}\n`); 464 }, 465 before(asyncId) { 466 const indentStr = ' '.repeat(indent); 467 fs.writeSync(fd, `${indentStr}before: ${asyncId}\n`); 468 indent += 2; 469 }, 470 after(asyncId) { 471 indent -= 2; 472 const indentStr = ' '.repeat(indent); 473 fs.writeSync(fd, `${indentStr}after: ${asyncId}\n`); 474 }, 475 destroy(asyncId) { 476 const indentStr = ' '.repeat(indent); 477 fs.writeSync(fd, `${indentStr}destroy: ${asyncId}\n`); 478 }, 479}).enable(); 480 481net.createServer(() => {}).listen(8080, () => { 482 // Let's wait 10ms before logging the server started. 483 setTimeout(() => { 484 console.log('>>>', async_hooks.executionAsyncId()); 485 }, 10); 486}); 487``` 488 489```cjs 490const async_hooks = require('node:async_hooks'); 491const fs = require('node:fs'); 492const net = require('node:net'); 493const { fd } = process.stdout; 494 495let indent = 0; 496async_hooks.createHook({ 497 init(asyncId, type, triggerAsyncId) { 498 const eid = async_hooks.executionAsyncId(); 499 const indentStr = ' '.repeat(indent); 500 fs.writeSync( 501 fd, 502 `${indentStr}${type}(${asyncId}):` + 503 ` trigger: ${triggerAsyncId} execution: ${eid}\n`); 504 }, 505 before(asyncId) { 506 const indentStr = ' '.repeat(indent); 507 fs.writeSync(fd, `${indentStr}before: ${asyncId}\n`); 508 indent += 2; 509 }, 510 after(asyncId) { 511 indent -= 2; 512 const indentStr = ' '.repeat(indent); 513 fs.writeSync(fd, `${indentStr}after: ${asyncId}\n`); 514 }, 515 destroy(asyncId) { 516 const indentStr = ' '.repeat(indent); 517 fs.writeSync(fd, `${indentStr}destroy: ${asyncId}\n`); 518 }, 519}).enable(); 520 521net.createServer(() => {}).listen(8080, () => { 522 // Let's wait 10ms before logging the server started. 523 setTimeout(() => { 524 console.log('>>>', async_hooks.executionAsyncId()); 525 }, 10); 526}); 527``` 528 529Output from only starting the server: 530 531```console 532TCPSERVERWRAP(5): trigger: 1 execution: 1 533TickObject(6): trigger: 5 execution: 1 534before: 6 535 Timeout(7): trigger: 6 execution: 6 536after: 6 537destroy: 6 538before: 7 539>>> 7 540 TickObject(8): trigger: 7 execution: 7 541after: 7 542before: 8 543after: 8 544``` 545 546As illustrated in the example, `executionAsyncId()` and `execution` each specify 547the value of the current execution context; which is delineated by calls to 548`before` and `after`. 549 550Only using `execution` to graph resource allocation results in the following: 551 552```console 553 root(1) 554 ^ 555 | 556TickObject(6) 557 ^ 558 | 559 Timeout(7) 560``` 561 562The `TCPSERVERWRAP` is not part of this graph, even though it was the reason for 563`console.log()` being called. This is because binding to a port without a host 564name is a _synchronous_ operation, but to maintain a completely asynchronous 565API the user's callback is placed in a `process.nextTick()`. Which is why 566`TickObject` is present in the output and is a 'parent' for `.listen()` 567callback. 568 569The graph only shows _when_ a resource was created, not _why_, so to track 570the _why_ use `triggerAsyncId`. Which can be represented with the following 571graph: 572 573```console 574 bootstrap(1) 575 | 576 ˅ 577TCPSERVERWRAP(5) 578 | 579 ˅ 580 TickObject(6) 581 | 582 ˅ 583 Timeout(7) 584``` 585 586#### `before(asyncId)` 587 588* `asyncId` {number} 589 590When an asynchronous operation is initiated (such as a TCP server receiving a 591new connection) or completes (such as writing data to disk) a callback is 592called to notify the user. The `before` callback is called just before said 593callback is executed. `asyncId` is the unique identifier assigned to the 594resource about to execute the callback. 595 596The `before` callback will be called 0 to N times. The `before` callback 597will typically be called 0 times if the asynchronous operation was cancelled 598or, for example, if no connections are received by a TCP server. Persistent 599asynchronous resources like a TCP server will typically call the `before` 600callback multiple times, while other operations like `fs.open()` will call 601it only once. 602 603#### `after(asyncId)` 604 605* `asyncId` {number} 606 607Called immediately after the callback specified in `before` is completed. 608 609If an uncaught exception occurs during execution of the callback, then `after` 610will run _after_ the `'uncaughtException'` event is emitted or a `domain`'s 611handler runs. 612 613#### `destroy(asyncId)` 614 615* `asyncId` {number} 616 617Called after the resource corresponding to `asyncId` is destroyed. It is also 618called asynchronously from the embedder API `emitDestroy()`. 619 620Some resources depend on garbage collection for cleanup, so if a reference is 621made to the `resource` object passed to `init` it is possible that `destroy` 622will never be called, causing a memory leak in the application. If the resource 623does not depend on garbage collection, then this will not be an issue. 624 625Using the destroy hook results in additional overhead because it enables 626tracking of `Promise` instances via the garbage collector. 627 628#### `promiseResolve(asyncId)` 629 630<!-- YAML 631added: v8.6.0 632--> 633 634* `asyncId` {number} 635 636Called when the `resolve` function passed to the `Promise` constructor is 637invoked (either directly or through other means of resolving a promise). 638 639`resolve()` does not do any observable synchronous work. 640 641The `Promise` is not necessarily fulfilled or rejected at this point if the 642`Promise` was resolved by assuming the state of another `Promise`. 643 644```js 645new Promise((resolve) => resolve(true)).then((a) => {}); 646``` 647 648calls the following callbacks: 649 650```text 651init for PROMISE with id 5, trigger id: 1 652 promise resolve 5 # corresponds to resolve(true) 653init for PROMISE with id 6, trigger id: 5 # the Promise returned by then() 654 before 6 # the then() callback is entered 655 promise resolve 6 # the then() callback resolves the promise by returning 656 after 6 657``` 658 659### `async_hooks.executionAsyncResource()` 660 661<!-- YAML 662added: 663 - v13.9.0 664 - v12.17.0 665--> 666 667* Returns: {Object} The resource representing the current execution. 668 Useful to store data within the resource. 669 670Resource objects returned by `executionAsyncResource()` are most often internal 671Node.js handle objects with undocumented APIs. Using any functions or properties 672on the object is likely to crash your application and should be avoided. 673 674Using `executionAsyncResource()` in the top-level execution context will 675return an empty object as there is no handle or request object to use, 676but having an object representing the top-level can be helpful. 677 678```mjs 679import { open } from 'node:fs'; 680import { executionAsyncId, executionAsyncResource } from 'node:async_hooks'; 681 682console.log(executionAsyncId(), executionAsyncResource()); // 1 {} 683open(new URL(import.meta.url), 'r', (err, fd) => { 684 console.log(executionAsyncId(), executionAsyncResource()); // 7 FSReqWrap 685}); 686``` 687 688```cjs 689const { open } = require('node:fs'); 690const { executionAsyncId, executionAsyncResource } = require('node:async_hooks'); 691 692console.log(executionAsyncId(), executionAsyncResource()); // 1 {} 693open(__filename, 'r', (err, fd) => { 694 console.log(executionAsyncId(), executionAsyncResource()); // 7 FSReqWrap 695}); 696``` 697 698This can be used to implement continuation local storage without the 699use of a tracking `Map` to store the metadata: 700 701```mjs 702import { createServer } from 'node:http'; 703import { 704 executionAsyncId, 705 executionAsyncResource, 706 createHook, 707} from 'async_hooks'; 708const sym = Symbol('state'); // Private symbol to avoid pollution 709 710createHook({ 711 init(asyncId, type, triggerAsyncId, resource) { 712 const cr = executionAsyncResource(); 713 if (cr) { 714 resource[sym] = cr[sym]; 715 } 716 }, 717}).enable(); 718 719const server = createServer((req, res) => { 720 executionAsyncResource()[sym] = { state: req.url }; 721 setTimeout(function() { 722 res.end(JSON.stringify(executionAsyncResource()[sym])); 723 }, 100); 724}).listen(3000); 725``` 726 727```cjs 728const { createServer } = require('node:http'); 729const { 730 executionAsyncId, 731 executionAsyncResource, 732 createHook, 733} = require('node:async_hooks'); 734const sym = Symbol('state'); // Private symbol to avoid pollution 735 736createHook({ 737 init(asyncId, type, triggerAsyncId, resource) { 738 const cr = executionAsyncResource(); 739 if (cr) { 740 resource[sym] = cr[sym]; 741 } 742 }, 743}).enable(); 744 745const server = createServer((req, res) => { 746 executionAsyncResource()[sym] = { state: req.url }; 747 setTimeout(function() { 748 res.end(JSON.stringify(executionAsyncResource()[sym])); 749 }, 100); 750}).listen(3000); 751``` 752 753### `async_hooks.executionAsyncId()` 754 755<!-- YAML 756added: v8.1.0 757changes: 758 - version: v8.2.0 759 pr-url: https://github.com/nodejs/node/pull/13490 760 description: Renamed from `currentId`. 761--> 762 763* Returns: {number} The `asyncId` of the current execution context. Useful to 764 track when something calls. 765 766```mjs 767import { executionAsyncId } from 'node:async_hooks'; 768import fs from 'node:fs'; 769 770console.log(executionAsyncId()); // 1 - bootstrap 771const path = '.'; 772fs.open(path, 'r', (err, fd) => { 773 console.log(executionAsyncId()); // 6 - open() 774}); 775``` 776 777```cjs 778const async_hooks = require('node:async_hooks'); 779const fs = require('node:fs'); 780 781console.log(async_hooks.executionAsyncId()); // 1 - bootstrap 782const path = '.'; 783fs.open(path, 'r', (err, fd) => { 784 console.log(async_hooks.executionAsyncId()); // 6 - open() 785}); 786``` 787 788The ID returned from `executionAsyncId()` is related to execution timing, not 789causality (which is covered by `triggerAsyncId()`): 790 791```js 792const server = net.createServer((conn) => { 793 // Returns the ID of the server, not of the new connection, because the 794 // callback runs in the execution scope of the server's MakeCallback(). 795 async_hooks.executionAsyncId(); 796 797}).listen(port, () => { 798 // Returns the ID of a TickObject (process.nextTick()) because all 799 // callbacks passed to .listen() are wrapped in a nextTick(). 800 async_hooks.executionAsyncId(); 801}); 802``` 803 804Promise contexts may not get precise `executionAsyncIds` by default. 805See the section on [promise execution tracking][]. 806 807### `async_hooks.triggerAsyncId()` 808 809* Returns: {number} The ID of the resource responsible for calling the callback 810 that is currently being executed. 811 812```js 813const server = net.createServer((conn) => { 814 // The resource that caused (or triggered) this callback to be called 815 // was that of the new connection. Thus the return value of triggerAsyncId() 816 // is the asyncId of "conn". 817 async_hooks.triggerAsyncId(); 818 819}).listen(port, () => { 820 // Even though all callbacks passed to .listen() are wrapped in a nextTick() 821 // the callback itself exists because the call to the server's .listen() 822 // was made. So the return value would be the ID of the server. 823 async_hooks.triggerAsyncId(); 824}); 825``` 826 827Promise contexts may not get valid `triggerAsyncId`s by default. See 828the section on [promise execution tracking][]. 829 830### `async_hooks.asyncWrapProviders` 831 832<!-- YAML 833added: 834 - v17.2.0 835 - v16.14.0 836--> 837 838* Returns: A map of provider types to the corresponding numeric id. 839 This map contains all the event types that might be emitted by the `async_hooks.init()` event. 840 841This feature suppresses the deprecated usage of `process.binding('async_wrap').Providers`. 842See: [DEP0111][] 843 844## Promise execution tracking 845 846By default, promise executions are not assigned `asyncId`s due to the relatively 847expensive nature of the [promise introspection API][PromiseHooks] provided by 848V8. This means that programs using promises or `async`/`await` will not get 849correct execution and trigger ids for promise callback contexts by default. 850 851```mjs 852import { executionAsyncId, triggerAsyncId } from 'node:async_hooks'; 853 854Promise.resolve(1729).then(() => { 855 console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`); 856}); 857// produces: 858// eid 1 tid 0 859``` 860 861```cjs 862const { executionAsyncId, triggerAsyncId } = require('node:async_hooks'); 863 864Promise.resolve(1729).then(() => { 865 console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`); 866}); 867// produces: 868// eid 1 tid 0 869``` 870 871Observe that the `then()` callback claims to have executed in the context of the 872outer scope even though there was an asynchronous hop involved. Also, 873the `triggerAsyncId` value is `0`, which means that we are missing context about 874the resource that caused (triggered) the `then()` callback to be executed. 875 876Installing async hooks via `async_hooks.createHook` enables promise execution 877tracking: 878 879```mjs 880import { createHook, executionAsyncId, triggerAsyncId } from 'node:async_hooks'; 881createHook({ init() {} }).enable(); // forces PromiseHooks to be enabled. 882Promise.resolve(1729).then(() => { 883 console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`); 884}); 885// produces: 886// eid 7 tid 6 887``` 888 889```cjs 890const { createHook, executionAsyncId, triggerAsyncId } = require('node:async_hooks'); 891 892createHook({ init() {} }).enable(); // forces PromiseHooks to be enabled. 893Promise.resolve(1729).then(() => { 894 console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`); 895}); 896// produces: 897// eid 7 tid 6 898``` 899 900In this example, adding any actual hook function enabled the tracking of 901promises. There are two promises in the example above; the promise created by 902`Promise.resolve()` and the promise returned by the call to `then()`. In the 903example above, the first promise got the `asyncId` `6` and the latter got 904`asyncId` `7`. During the execution of the `then()` callback, we are executing 905in the context of promise with `asyncId` `7`. This promise was triggered by 906async resource `6`. 907 908Another subtlety with promises is that `before` and `after` callbacks are run 909only on chained promises. That means promises not created by `then()`/`catch()` 910will not have the `before` and `after` callbacks fired on them. For more details 911see the details of the V8 [PromiseHooks][] API. 912 913## JavaScript embedder API 914 915Library developers that handle their own asynchronous resources performing tasks 916like I/O, connection pooling, or managing callback queues may use the 917`AsyncResource` JavaScript API so that all the appropriate callbacks are called. 918 919### Class: `AsyncResource` 920 921The documentation for this class has moved [`AsyncResource`][]. 922 923## Class: `AsyncLocalStorage` 924 925The documentation for this class has moved [`AsyncLocalStorage`][]. 926 927[DEP0111]: deprecations.md#dep0111-processbinding 928[Diagnostics Channel]: diagnostics_channel.md 929[Hook Callbacks]: #hook-callbacks 930[PromiseHooks]: https://docs.google.com/document/d/1rda3yKGHimKIhg5YeoAmCOtyURgsbTH_qaYR79FELlk/edit 931[`AsyncHook`]: #class-asynchook 932[`AsyncLocalStorage`]: async_context.md#class-asynclocalstorage 933[`AsyncResource`]: async_context.md#class-asyncresource 934[`Worker`]: worker_threads.md#class-worker 935[`after` callback]: #afterasyncid 936[`before` callback]: #beforeasyncid 937[`createHook`]: #async_hookscreatehookcallbacks 938[`destroy` callback]: #destroyasyncid 939[`executionAsyncResource`]: #async_hooksexecutionasyncresource 940[`init` callback]: #initasyncid-type-triggerasyncid-resource 941[`process.getActiveResourcesInfo()`]: process.md#processgetactiveresourcesinfo 942[`promiseResolve` callback]: #promiseresolveasyncid 943[promise execution tracking]: #promise-execution-tracking 944