From 757fa4e07e53fdf2de44a7077b8b575324bc4a85 Mon Sep 17 00:00:00 2001 From: Ali Ijaz Sheikh Date: Fri, 2 Feb 2018 15:48:09 -0800 Subject: [PATCH] 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 --- doc/api/async_hooks.md | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md index 781509900cc4c9..e0922fcd1134f5 100644 --- a/doc/api/async_hooks.md +++ b/doc/api/async_hooks.md @@ -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