Skip to content

Commit 73b8272

Browse files
committed
doc: some async_hooks docs improvements
1 parent dd52cad commit 73b8272

File tree

1 file changed

+81
-43
lines changed

1 file changed

+81
-43
lines changed

doc/api/async_hooks.md

Lines changed: 81 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,11 @@ function destroy(asyncId) { }
7373
added: REPLACEME
7474
-->
7575

76-
* `callbacks` {Object} the callbacks to register
76+
* `callbacks` {Object} the [Hook Callbacks][] to register
77+
* `init` {Function} The [`init` callback][].
78+
* `before` {Function} The [`before` callback][].
79+
* `after` {Function} The [`after` callback][].
80+
* `destroy` {Function} The [`destroy` callback][].
7781
* Returns: `{AsyncHook}` instance used for disabling and enabling hooks
7882

7983
Registers functions to be called for different lifetime events of each async
@@ -87,6 +91,31 @@ be tracked then only the `destroy` callback needs to be passed. The
8791
specifics of all functions that can be passed to `callbacks` is in the section
8892
[`Hook Callbacks`][].
8993

94+
```js
95+
const async_hooks = require('async_hooks');
96+
97+
const asyncHook = async_hooks.createHook({
98+
init(asyncId, type, triggerAsyncId, resource) { },
99+
destroy(asyncId) { }
100+
});
101+
```
102+
103+
Note that the callbacks will be inherited via the prototype chain:
104+
105+
```js
106+
class MyAsyncCallbacks {
107+
init(asyncId, type, triggerAsyncId, resource) { }
108+
destroy(asyncId) {}
109+
}
110+
111+
class MyAddedCallbacks extends MyAsyncCallbacks {
112+
before(asyncId) { }
113+
after(asyncId) { }
114+
}
115+
116+
const asyncHook = async_hooks.createHook(new MyAddedCallbacks());
117+
```
118+
90119
##### Error Handling
91120

92121
If any `AsyncHook` callbacks throw, the application will print the stack trace
@@ -187,11 +216,12 @@ require('net').createServer().listen(function() { this.close(); });
187216
clearTimeout(setTimeout(() => {}, 10));
188217
```
189218

190-
Every new resource is assigned a unique ID.
219+
Every new resource is assigned an ID that is unique within the scope of the
220+
current process.
191221

192222
###### `type`
193223

194-
The `type` is a string that represents the type of resource that caused
224+
The `type` is a string identifying the type of resource that caused
195225
`init` to be called. Generally, it will correspond to the name of the
196226
resource's constructor.
197227

@@ -214,8 +244,8 @@ when listening to the hooks.
214244

215245
###### `triggerId`
216246

217-
`triggerAsyncId` is the `asyncId` of the resource that caused (or "triggered") the
218-
new resource to initialize and that caused `init` to call. This is different
247+
`triggerAsyncId` is the `asyncId` of the resource that caused (or "triggered")
248+
the new resource to initialize and that caused `init` to call. This is different
219249
from `async_hooks.executionAsyncId()` that only shows *when* a resource was
220250
created, while `triggerAsyncId` shows *why* a resource was created.
221251

@@ -254,25 +284,25 @@ propagating what resource is responsible for the new resource's existence.
254284
###### `resource`
255285

256286
`resource` is an object that represents the actual resource. This can contain
257-
useful information such as the hostname for the `GETADDRINFOREQWRAP` resource
258-
type, which will be used when looking up the ip for the hostname in
259-
`net.Server.listen`. The API for getting this information is currently not
260-
considered public, but using the Embedder API users can provide and document
261-
their own resource objects. Such as resource object could for example contain
262-
the SQL query being executed.
287+
useful information that can vary based on the value of `type`. For instance,
288+
for the `GETADDRINFOREQWRAP` resource type, it provides the hostname used when
289+
looking up the IP address for the hostname in `net.Server.listen()`. The API for
290+
accessing this information is currently not considered public, but using the
291+
Embedder API, users can provide and document their own resource objects. Such
292+
a resource object could for example contain the SQL query being executed.
263293

264294
In the case of Promises, the `resource` object will have `promise` property
265295
that refers to the Promise that is being initialized, and a `parentId` property
266-
that equals the `asyncId` of a parent Promise, if there is one, and
296+
that set to the `asyncId` of a parent Promise, if there is one, and
267297
`undefined` otherwise. For example, in the case of `b = a.then(handler)`,
268298
`a` is considered a parent Promise of `b`.
269299

270300
*Note*: In some cases the resource object is reused for performance reasons,
271301
it is thus not safe to use it as a key in a `WeakMap` or add properties to it.
272302

273-
###### asynchronous context example
303+
###### Asynchronous context example
274304

275-
Below is another example with additional information about the calls to
305+
Following is an example with additional information about the calls to
276306
`init` between the `before` and `after` calls, specifically what the
277307
callback to `listen()` will look like. The output formatting is slightly more
278308
elaborate to make calling context easier to see.
@@ -348,10 +378,11 @@ Only using `execution` to graph resource allocation results in the following:
348378
TTYWRAP(6) -> Timeout(4) -> TIMERWRAP(5) -> TickObject(3) -> root(1)
349379
```
350380

351-
The `TCPWRAP` isn't part of this graph; even though it was the reason for
381+
The `TCPWRAP` is not part of this graph; even though it was the reason for
352382
`console.log()` being called. This is because binding to a port without a
353-
hostname is actually synchronous, but to maintain a completely asynchronous API
354-
the user's callback is placed in a `process.nextTick()`.
383+
hostname is a *synchronous* operation within Node.js, but to maintain a
384+
completely asynchronous API the user's callback is placed in a
385+
`process.nextTick()`.
355386

356387
The graph only shows *when* a resource was created, not *why*, so to track
357388
the *why* use `triggerAsyncId`.
@@ -369,9 +400,10 @@ resource about to execute the callback.
369400

370401
The `before` callback will be called 0 to N times. The `before` callback
371402
will typically be called 0 times if the asynchronous operation was cancelled
372-
or for example if no connections are received by a TCP server. Asynchronous
373-
like the TCP server will typically call the `before` callback multiple times,
374-
while other operations like `fs.open()` will only call it once.
403+
or, for example, if no connections are received by a TCP server. Persistent
404+
asynchronous resources like a TCP server will typically call the `before`
405+
callback multiple times, while other operations like `fs.open()` will only call
406+
it only once.
375407

376408

377409
##### `after(asyncId)`
@@ -381,30 +413,33 @@ while other operations like `fs.open()` will only call it once.
381413
Called immediately after the callback specified in `before` is completed.
382414

383415
*Note:* If an uncaught exception occurs during execution of the callback then
384-
`after` will run after the `'uncaughtException'` event is emitted or a
416+
`after` will run *after* the `'uncaughtException'` event is emitted or a
385417
`domain`'s handler runs.
386418

387419

388420
##### `destroy(asyncId)`
389421

390422
* `asyncId` {number}
391423

392-
Called after the resource corresponding to `asyncId` is destroyed. It is also called
393-
asynchronously from the embedder API `emitDestroy()`.
424+
Called after the resource corresponding to `asyncId` is destroyed. It is also
425+
called asynchronously from the embedder API `emitDestroy()`.
394426

395-
*Note:* Some resources depend on GC for cleanup, so if a reference is made to
396-
the `resource` object passed to `init` it's possible that `destroy` is
397-
never called, causing a memory leak in the application. Of course if
398-
the resource doesn't depend on GC then this isn't an issue.
427+
*Note:* Some resources depend on garbage collection for cleanup, so if a
428+
reference is made to the `resource` object passed to `init` it is possible that
429+
`destroy` will never be called, causing a memory leak in the application. If
430+
the resource does not depend on garbage collection then this will not be an
431+
issue.
399432

400433
#### `async_hooks.executionAsyncId()`
401434

402-
* Returns {number} the `asyncId` of the current execution context. Useful to track
403-
when something calls.
435+
* Returns {number} the `asyncId` of the current execution context. Useful to
436+
track when something calls.
404437

405438
For example:
406439

407440
```js
441+
const async_hooks = require('async_hooks');
442+
408443
console.log(async_hooks.executionAsyncId()); // 1 - bootstrap
409444
fs.open(path, 'r', (err, fd) => {
410445
console.log(async_hooks.executionAsyncId()); // 6 - open()
@@ -453,10 +488,9 @@ const server = net.createServer((conn) => {
453488

454489
## JavaScript Embedder API
455490

456-
Library developers that handle their own I/O, a connection pool, or
457-
callback queues will need to hook into the AsyncWrap API so that all the
458-
appropriate callbacks are called. To accommodate this a JavaScript API is
459-
provided.
491+
Library developers that handle their own asychronous resources performing tasks
492+
like I/O, connection pooling, or managing callback queues may use the AsyncWrap
493+
JavaScript API so that all the appropriate callbacks are called.
460494

461495
### `class AsyncResource()`
462496

@@ -466,9 +500,9 @@ own resources.
466500

467501
The `init` hook will trigger when an `AsyncResource` is instantiated.
468502

469-
It is important that `before`/`after` calls are unwound
503+
*Note*: It is important that `before`/`after` calls are unwound
470504
in the same order they are called. Otherwise an unrecoverable exception
471-
will occur and node will abort.
505+
will occur and the process will abort.
472506

473507
The following is an overview of the `AsyncResource` API.
474508

@@ -500,8 +534,8 @@ asyncResource.triggerAsyncId();
500534

501535
* arguments
502536
* `type` {string} the type of ascyc event
503-
* `triggerAsyncId` {number} the ID of the execution context that created this async
504-
event
537+
* `triggerAsyncId` {number} the ID of the execution context that created this
538+
async event
505539

506540
Example usage:
507541

@@ -531,9 +565,9 @@ class DBQuery extends AsyncResource {
531565

532566
* Returns {undefined}
533567

534-
Call all `before` callbacks and let them know a new asynchronous execution
535-
context is being entered. If nested calls to `emitBefore()` are made, the stack
536-
of `asyncId`s will be tracked and properly unwound.
568+
Call all `before` callbacks to notify that a new asynchronous execution context
569+
is being entered. If nested calls to `emitBefore()` are made, the stack of
570+
`asyncId`s will be tracked and properly unwound.
537571

538572
#### `asyncResource.emitAfter()`
539573

@@ -542,9 +576,9 @@ of `asyncId`s will be tracked and properly unwound.
542576
Call all `after` callbacks. If nested calls to `emitBefore()` were made, then
543577
make sure the stack is unwound properly. Otherwise an error will be thrown.
544578

545-
If the user's callback throws an exception then `emitAfter()` will
546-
automatically be called for all `asyncId`s on the stack if the error is handled by
547-
a domain or `'uncaughtException'` handler.
579+
If the user's callback throws an exception, `emitAfter()` will automatically be
580+
called for all `asyncId`s on the stack if the error is handled by a domain or
581+
`'uncaughtException'` handler.
548582

549583
#### `asyncResource.emitDestroy()`
550584

@@ -564,4 +598,8 @@ never be called.
564598
* Returns {number} the same `triggerAsyncId` that is passed to the `AsyncResource`
565599
constructor.
566600

601+
[`after` callback]: #async_hooks_after_asyncid
602+
[`before` callback]: #async_hooks_before_asyncid
603+
[`destroy` callback]: #async_hooks_before_asyncid
567604
[`Hook Callbacks`]: #async_hooks_hook_callbacks
605+
[`init` callback]: #async_hooks_init_asyncid_type_triggerasyncid_resource

0 commit comments

Comments
 (0)