Skip to content

Commit

Permalink
doc: expand on promises and async_hooks
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
ofrobots authored and gibfahn committed Apr 13, 2018
1 parent 73adadd commit 25db460
Showing 1 changed file with 56 additions and 0 deletions.
56 changes: 56 additions & 0 deletions doc/api/async_hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,9 @@ const server = net.createServer(function onConnection(conn) {
});
```

Note that promise contexts may not get precise executionAsyncIds by default.
See the section on [promise execution tracking][].

#### `async_hooks.triggerAsyncId()`

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

Note that promise contexts may not get valid triggerAsyncIds by default. See
the section on [promise execution tracking][].

## Promise execution tracking

By default, promise executions are not assigned asyncIds due to the relatively
expensive nature of the [promise introspection API][PromiseHooks] provided by
V8. This means that programs using promises or `async`/`await` will not get
correct execution and trigger ids for promise callback contexts by default.

Here's an example:

```js
const ah = require('async_hooks');
Promise.resolve(1729).then(() => {
console.log(`eid ${ah.executionAsyncId()} tid ${ah.triggerAsyncId()}`);
});
// produces:
// eid 1 tid 0
```

Observe that the `then` callback claims to have executed in the context of the
outer scope even though there was an asynchronous hop involved. Also note that
the triggerAsyncId value is 0, which means that we are missing context about the
resource that caused (triggered) the `then` callback to be executed.

Installing async hooks via `async_hooks.createHook` enables promise execution
tracking. Example:

```js
const ah = require('async_hooks');
ah.createHook({ init() {} }).enable(); // forces PromiseHooks to be enabled.
Promise.resolve(1729).then(() => {
console.log(`eid ${ah.executionAsyncId()} tid ${ah.triggerAsyncId()}`);
});
// produces:
// eid 7 tid 6
```

In this example, adding any actual hook function enabled the tracking of
promises. There are two promises in the example above; the promise created by
`Promise.resolve()` and the promise returned by the call to `then`. In the
example above, the first promise got the asyncId 6 and the latter got asyncId 7.
During the execution of the `then` callback, we are executing in the context of
promise with asyncId 7. This promise was triggered by async resource 6.

Another subtlety with promises is that `before` and `after` callbacks are run
only on chained promises. That means promises not created by `then`/`catch` will
not have the `before` and `after` callbacks fired on them. For more details see
the details of the V8 [PromiseHooks][] API.

## JavaScript Embedder API

Library developers that handle their own asynchronous resources performing tasks
Expand Down Expand Up @@ -655,3 +709,5 @@ constructor.
[`destroy` callback]: #async_hooks_destroy_asyncid
[`init` callback]: #async_hooks_init_asyncid_type_triggerasyncid_resource
[Hook Callbacks]: #async_hooks_hook_callbacks
[PromiseHooks]: https://docs.google.com/document/d/1rda3yKGHimKIhg5YeoAmCOtyURgsbTH_qaYR79FELlk
[promise execution tracking]: #async_hooks_promise_execution_tracking

0 comments on commit 25db460

Please sign in to comment.