@@ -509,6 +509,9 @@ const server = net.createServer(function onConnection(conn) { |
509 | 509 | }); |
510 | 510 | ``` |
511 | 511 |
|
| 512 | +Note that promise contexts may not get precise executionAsyncIds by default. |
| 513 | +See the section on [promise execution tracking][]. |
| 514 | + |
512 | 515 | #### `async_hooks.triggerAsyncId()` |
513 | 516 |
|
514 | 517 | * Returns: {number} The ID of the resource responsible for calling the callback |
@@ -531,6 +534,57 @@ const server = net.createServer((conn) => { |
531 | 534 | }); |
532 | 535 | ``` |
533 | 536 |
|
| 537 | +Note that promise contexts may not get valid triggerAsyncIds by default. See |
| 538 | +the section on [promise execution tracking][]. |
| 539 | + |
| 540 | +## Promise execution tracking |
| 541 | + |
| 542 | +By default, promise executions are not assigned asyncIds due to the relatively |
| 543 | +expensive nature of the [promise introspection API][PromiseHooks] provided by |
| 544 | +V8. This means that programs using promises or `async`/`await` will not get |
| 545 | +correct execution and trigger ids for promise callback contexts by default. |
| 546 | + |
| 547 | +Here's an example: |
| 548 | + |
| 549 | +```js |
| 550 | +const ah = require('async_hooks'); |
| 551 | +Promise.resolve(1729).then(() => { |
| 552 | + console.log(`eid ${ah.executionAsyncId()} tid ${ah.triggerAsyncId()}`); |
| 553 | +}); |
| 554 | +// produces: |
| 555 | +// eid 1 tid 0 |
| 556 | +``` |
| 557 | + |
| 558 | +Observe that the `then` callback claims to have executed in the context of the |
| 559 | +outer scope even though there was an asynchronous hop involved. Also note that |
| 560 | +the triggerAsyncId value is 0, which means that we are missing context about the |
| 561 | +resource that caused (triggered) the `then` callback to be executed. |
| 562 | + |
| 563 | +Installing async hooks via `async_hooks.createHook` enables promise execution |
| 564 | +tracking. Example: |
| 565 | + |
| 566 | +```js |
| 567 | +const ah = require('async_hooks'); |
| 568 | +ah.createHook({ init() {} }).enable(); // forces PromiseHooks to be enabled. |
| 569 | +Promise.resolve(1729).then(() => { |
| 570 | + console.log(`eid ${ah.executionAsyncId()} tid ${ah.triggerAsyncId()}`); |
| 571 | +}); |
| 572 | +// produces: |
| 573 | +// eid 7 tid 6 |
| 574 | +``` |
| 575 | + |
| 576 | +In this example, adding any actual hook function enabled the tracking of |
| 577 | +promises. There are two promises in the example above; the promise created by |
| 578 | +`Promise.resolve()` and the promise returned by the call to `then`. In the |
| 579 | +example above, the first promise got the asyncId 6 and the latter got asyncId 7. |
| 580 | +During the execution of the `then` callback, we are executing in the context of |
| 581 | +promise with asyncId 7. This promise was triggered by async resource 6. |
| 582 | + |
| 583 | +Another subtlety with promises is that `before` and `after` callbacks are run |
| 584 | +only on chained promises. That means promises not created by `then`/`catch` will |
| 585 | +not have the `before` and `after` callbacks fired on them. For more details see |
| 586 | +the details of the V8 [PromiseHooks][] API. |
| 587 | + |
534 | 588 | ## JavaScript Embedder API |
535 | 589 |
|
536 | 590 | Library developers that handle their own asynchronous resources performing tasks |
@@ -655,3 +709,5 @@ constructor. |
655 | 709 | [`destroy` callback]: #async_hooks_destroy_asyncid |
656 | 710 | [`init` callback]: #async_hooks_init_asyncid_type_triggerasyncid_resource |
657 | 711 | [Hook Callbacks]: #async_hooks_hook_callbacks |
| 712 | +[PromiseHooks]: https://docs.google.com/document/d/1rda3yKGHimKIhg5YeoAmCOtyURgsbTH_qaYR79FELlk |
| 713 | +[promise execution tracking]: #async_hooks_promise_execution_tracking |