Skip to content

Commit 2418c86

Browse files
ofrobotsMylesBorins
authored andcommitted
doc: expand on promises and async_hooks
AsyncHooks have a few subtleties with being able to track promises. This commit adds a section to the docs that explains things the issues. PR-URL: #18540 Fixes: #18520 Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent d258112 commit 2418c86

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

doc/api/async_hooks.md

+56
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,9 @@ const server = net.createServer(function onConnection(conn) {
509509
});
510510
```
511511

512+
Note that promise contexts may not get precise executionAsyncIds by default.
513+
See the section on [promise execution tracking][].
514+
512515
#### `async_hooks.triggerAsyncId()`
513516

514517
* Returns: {number} The ID of the resource responsible for calling the callback
@@ -531,6 +534,57 @@ const server = net.createServer((conn) => {
531534
});
532535
```
533536

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+
534588
## JavaScript Embedder API
535589

536590
Library developers that handle their own asynchronous resources performing tasks
@@ -655,3 +709,5 @@ constructor.
655709
[`destroy` callback]: #async_hooks_destroy_asyncid
656710
[`init` callback]: #async_hooks_init_asyncid_type_triggerasyncid_resource
657711
[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

0 commit comments

Comments
 (0)