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: https://github.com/nodejs/node/pull/18540
Fixes: https://github.com/nodejs/node/issues/18520
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Ali Ijaz Sheikh 2018-02-02 15:48:09 -08:00
parent 47f664ee88
commit d4b605b990

View File

@ -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
@ -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
@ -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