Usage in Deno
```typescript import * as mod from "node:node__async_hooks.d.ts"; ```We strongly discourage the use of the `async_hooks` API.
Other APIs that can cover most of its use cases include:
* [`AsyncLocalStorage`](https://nodejs.org/docs/latest-v22.x/api/async_context.html#class-asynclocalstorage) tracks async context
* [`process.getActiveResourcesInfo()`](https://nodejs.org/docs/latest-v22.x/api/process.html#processgetactiveresourcesinfo) tracks active resources
The `node:async_hooks` module provides an API to track asynchronous resources.
It can be accessed using:
```js
import async_hooks from 'node:async_hooks';
```
c
AsyncLocalStorage
This class creates stores that stay coherent through asynchronous operations.
While you can create your own implementation on top of the `node:async_hooks` module, `AsyncLocalStorage` should be preferred as it is a performant and memory
safe implementation that involves significant optimizations that are non-obvious
to implement.
The following example uses `AsyncLocalStorage` to build a simple logger
that assigns IDs to incoming HTTP requests and includes them in messages
logged within each request.
```js
import http from 'node:http';
import { AsyncLocalStorage } from 'node:async_hooks';
const asyncLocalStorage = new AsyncLocalStorage();
function logWithId(msg) {
const id = asyncLocalStorage.getStore();
console.log(`${id !== undefined ? id : '-'}:`, msg);
}
let idSeq = 0;
http.createServer((req, res) => {
asyncLocalStorage.run(idSeq++, () => {
logWithId('start');
// Imagine any chain of async operations here
setImmediate(() => {
logWithId('finish');
res.end();
});
});
}).listen(8080);
http.get('http://localhost:8080');
http.get('http://localhost:8080');
// Prints:
// 0: start
// 1: start
// 0: finish
// 1: finish
```
Each instance of `AsyncLocalStorage` maintains an independent storage context.
Multiple instances can safely exist simultaneously without risk of interfering
with each other's data.
c
AsyncResource
> [!WARNING] Deno compatibility
> The AsyncResource implementation is a non-functional stub.
The class `AsyncResource` is designed to be extended by the embedder's async
resources. Using this, users can easily trigger the lifetime events of their
own resources.
The `init` hook will trigger when an `AsyncResource` is instantiated.
The following is an overview of the `AsyncResource` API.
```js
import { AsyncResource, executionAsyncId } from 'node:async_hooks';
// AsyncResource() is meant to be extended. Instantiating a
// new AsyncResource() also triggers init. If triggerAsyncId is omitted then
// async_hook.executionAsyncId() is used.
const asyncResource = new AsyncResource(
type, { triggerAsyncId: executionAsyncId(), requireManualDestroy: false },
);
// Run a function in the execution context of the resource. This will
// * establish the context of the resource
// * trigger the AsyncHooks before callbacks
// * call the provided function `fn` with the supplied arguments
// * trigger the AsyncHooks after callbacks
// * restore the original execution context
asyncResource.runInAsyncScope(fn, thisArg, ...args);
// Call AsyncHooks destroy callbacks.
asyncResource.emitDestroy();
// Return the unique ID assigned to the AsyncResource instance.
asyncResource.asyncId();
// Return the trigger ID for the AsyncResource instance.
asyncResource.triggerAsyncId();
```
f
createHook
> [!WARNING] Deno compatibility
> The createHook implementation is a non-functional stub.
Registers functions to be called for different lifetime events of each async
operation.
The callbacks `init()`/`before()`/`after()`/`destroy()` are called for the
respective asynchronous event during a resource's lifetime.
All callbacks are optional. For example, if only resource cleanup needs to
be tracked, then only the `destroy` callback needs to be passed. The
specifics of all functions that can be passed to `callbacks` is in the `Hook Callbacks` section.
```js
import { createHook } from 'node:async_hooks';
const asyncHook = createHook({
init(asyncId, type, triggerAsyncId, resource) { },
destroy(asyncId) { },
});
```
The callbacks will be inherited via the prototype chain:
```js
class MyAsyncCallbacks {
init(asyncId, type, triggerAsyncId, resource) { }
destroy(asyncId) {}
}
class MyAddedCallbacks extends MyAsyncCallbacks {
before(asyncId) { }
after(asyncId) { }
}
const asyncHook = async_hooks.createHook(new MyAddedCallbacks());
```
Because promises are asynchronous resources whose lifecycle is tracked
via the async hooks mechanism, the `init()`, `before()`, `after()`, and`destroy()` callbacks _must not_ be async functions that return promises.
f
executionAsyncId
> [!WARNING] Deno compatibility
> The executionAsyncId implementation is a non-functional stub.
```js
import { executionAsyncId } from 'node:async_hooks';
import fs from 'node:fs';
console.log(executionAsyncId()); // 1 - bootstrap
const path = '.';
fs.open(path, 'r', (err, fd) => {
console.log(executionAsyncId()); // 6 - open()
});
```
The ID returned from `executionAsyncId()` is related to execution timing, not
causality (which is covered by `triggerAsyncId()`):
```js
const server = net.createServer((conn) => {
// Returns the ID of the server, not of the new connection, because the
// callback runs in the execution scope of the server's MakeCallback().
async_hooks.executionAsyncId();
}).listen(port, () => {
// Returns the ID of a TickObject (process.nextTick()) because all
// callbacks passed to .listen() are wrapped in a nextTick().
async_hooks.executionAsyncId();
});
```
Promise contexts may not get precise `executionAsyncIds` by default.
See the section on [promise execution tracking](https://nodejs.org/docs/latest-v22.x/api/async_hooks.html#promise-execution-tracking).
f
executionAsyncResource
Resource objects returned by `executionAsyncResource()` are most often internal
Node.js handle objects with undocumented APIs. Using any functions or properties
on the object is likely to crash your application and should be avoided.
Using `executionAsyncResource()` in the top-level execution context will
return an empty object as there is no handle or request object to use,
but having an object representing the top-level can be helpful.
```js
import { open } from 'node:fs';
import { executionAsyncId, executionAsyncResource } from 'node:async_hooks';
console.log(executionAsyncId(), executionAsyncResource()); // 1 {}
open(new URL(import.meta.url), 'r', (err, fd) => {
console.log(executionAsyncId(), executionAsyncResource()); // 7 FSReqWrap
});
```
This can be used to implement continuation local storage without the
use of a tracking `Map` to store the metadata:
```js
import { createServer } from 'node:http';
import {
executionAsyncId,
executionAsyncResource,
createHook,
} from 'node:async_hooks';
const sym = Symbol('state'); // Private symbol to avoid pollution
createHook({
init(asyncId, type, triggerAsyncId, resource) {
const cr = executionAsyncResource();
if (cr) {
resource[sym] = cr[sym];
}
},
}).enable();
const server = createServer((req, res) => {
executionAsyncResource()[sym] = { state: req.url };
setTimeout(function() {
res.end(JSON.stringify(executionAsyncResource()[sym]));
}, 100);
}).listen(3000);
```
f
triggerAsyncId
```js
const server = net.createServer((conn) => {
// The resource that caused (or triggered) this callback to be called
// was that of the new connection. Thus the return value of triggerAsyncId()
// is the asyncId of "conn".
async_hooks.triggerAsyncId();
}).listen(port, () => {
// Even though all callbacks passed to .listen() are wrapped in a nextTick()
// the callback itself exists because the call to the server's .listen()
// was made. So the return value would be the ID of the server.
async_hooks.triggerAsyncId();
});
```
Promise contexts may not get valid `triggerAsyncId`s by default. See
the section on [promise execution tracking](https://nodejs.org/docs/latest-v22.x/api/async_hooks.html#promise-execution-tracking).
I
I