async_hooks
Мы настоятельно не рекомендуем использовать API async_hooks. К большинству сценариев его использования можно применить другие API, в том числе:
-
AsyncLocalStorageотслеживает асинхронный контекст -
process.getActiveResourcesInfo()отслеживает активные ресурсы
Модуль node:async_hooks предоставляет API для отслеживания асинхронных ресурсов. Его можно подключить с помощью:
import async_hooks from 'node:async_hooks';
Использование в Deno
import * as mod from "node:async_hooks";
класс AsyncLocalStorage
Использование в Deno
import { AsyncLocalStorage } from "node:async_hooks";
Этот класс создает хранилища, сохраняющие согласованность при выполнении асинхронных операций.
Хотя можно создать собственную реализацию на основе модуля node:async_hooks, предпочтительно использовать AsyncLocalStorage — это производительная реализация с безопасным управлением памятью, включающая существенные оптимизации, которые сложно реализовать самостоятельно.
В следующем примере AsyncLocalStorage используется для создания простого средства ведения журнала, которое присваивает идентификаторы входящим HTTP-запросам и добавляет их в сообщения, записываемые в журнал в рамках каждого запроса.
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
Каждый экземпляр AsyncLocalStorage поддерживает независимый контекст хранилища. Одновременно могут безопасно существовать несколько экземпляров без риска взаимного влияния их данных.
Параметры типа
T Методы
disable(): void Отключает экземпляр AsyncLocalStorage. Все последующие вызовы asyncLocalStorage.getStore() будут возвращать undefined до тех пор, пока снова не будет вызван asyncLocalStorage.run() или asyncLocalStorage.enterWith().
При вызове asyncLocalStorage.disable() будут завершены все текущие контексты, связанные с экземпляром.
Перед сборкой мусора для asyncLocalStorage необходимо вызвать asyncLocalStorage.disable(). Это не относится к хранилищам, предоставляемым asyncLocalStorage, поскольку такие объекты собираются вместе с соответствующими асинхронными ресурсами.
Используйте этот метод, когда asyncLocalStorage больше не используется в текущем процессе.
Переключается в контекст на оставшуюся часть текущего синхронного выполнения, а затем сохраняет хранилище при всех последующих асинхронных вызовах.
Пример:
const store = { id: 1 };
// Replaces previous store with the given store object
asyncLocalStorage.enterWith(store);
asyncLocalStorage.getStore(); // Returns the store object
someAsyncOperation(() => {
asyncLocalStorage.getStore(); // Returns the same object
});
Этот переход будет действовать в течение всего синхронного выполнения. Это означает, что если, например, контекст был открыт в обработчике события, последующие обработчики событий также будут выполняться в этом контексте, если только они явно не привязаны к другому контексту с помощью AsyncResource. Поэтому следует предпочитать run() вместо enterWith(), если только нет веских причин использовать последний метод.
const store = { id: 1 };
emitter.on('my-event', () => {
asyncLocalStorage.enterWith(store);
});
emitter.on('my-event', () => {
asyncLocalStorage.getStore(); // Returns the same object
});
asyncLocalStorage.getStore(); // Returns undefined
emitter.emit('my-event');
asyncLocalStorage.getStore(); // Returns the same object
exit<
R,
TArgs extends any[],
>(
callback: (...args: TArgs) => R,
...args: TArgs,
): R Синхронно выполняет функцию вне контекста и возвращает ее результат. Хранилище недоступно внутри функции обратного вызова и асинхронных операций, созданных внутри нее. Любой вызов getStore() внутри функции обратного вызова всегда будет возвращать undefined.
Необязательные аргументы args передаются функции обратного вызова.
Если функция обратного вызова выбрасывает ошибку, exit() также выбрасывает эту ошибку. Этот вызов не влияет на трассировку стека, а контекст восстанавливается.
Пример:
// Within a call to run
try {
asyncLocalStorage.getStore(); // Returns the store object or value
asyncLocalStorage.exit(() => {
asyncLocalStorage.getStore(); // Returns undefined
throw new Error();
});
} catch (e) {
asyncLocalStorage.getStore(); // Returns the same object or value
// The error will be caught here
}
Возвращает текущее хранилище. Если вызван вне асинхронного контекста, инициализированного вызовом asyncLocalStorage.run() или asyncLocalStorage.enterWith(), возвращает undefined.
Синхронно выполняет функцию в контексте и возвращает ее результат. Хранилище недоступно вне функции обратного вызова. Оно доступно для любых асинхронных операций, созданных внутри функции обратного вызова.
Необязательные аргументы args передаются функции обратного вызова.
Если функция обратного вызова выбрасывает ошибку, run() также выбрасывает эту ошибку. Этот вызов не влияет на трассировку стека, а контекст закрывается.
Пример:
const store = { id: 2 };
try {
asyncLocalStorage.run(store, () => {
asyncLocalStorage.getStore(); // Returns the store object
setTimeout(() => {
asyncLocalStorage.getStore(); // Returns the store object
}, 200);
throw new Error();
});
} catch (e) {
asyncLocalStorage.getStore(); // Returns undefined
// The error will be caught here
}
Статические методы
bind<Func extends (...args: any[]) => any>(fn: Func): Func Привязывает указанную функцию к текущему контексту выполнения.
snapshot(): <
R,
TArgs extends any[],
>(
fn: (...args: TArgs) => R,
...args: TArgs,
) => R Сохраняет текущий контекст выполнения и возвращает функцию, принимающую функцию в качестве аргумента. При каждом вызове возвращенная функция вызывает переданную ей функцию в сохраненном контексте.
const asyncLocalStorage = new AsyncLocalStorage();
const runInAsyncScope = asyncLocalStorage.run(123, () => AsyncLocalStorage.snapshot());
const result = asyncLocalStorage.run(321, () => runInAsyncScope(() => asyncLocalStorage.getStore()));
console.log(result); // returns 123
AsyncLocalStorage.snapshot() может заменить AsyncResource для простого отслеживания асинхронного контекста, например:
class Foo {
#runInAsyncScope = AsyncLocalStorage.snapshot();
get() { return this.#runInAsyncScope(() => asyncLocalStorage.getStore()); }
}
const foo = asyncLocalStorage.run(123, () => new Foo());
console.log(asyncLocalStorage.run(321, () => foo.get())); // returns 123
класс AsyncResource
Использование в Deno
import { AsyncResource } from "node:async_hooks";
Реализация AsyncResource представляет собой неработающую заглушку.
Класс AsyncResource предназначен для расширения асинхронными ресурсами встраиваемой среды. Это позволяет пользователям легко инициировать события жизненного цикла собственных ресурсов.
Хук init срабатывает при создании экземпляра AsyncResource.
Ниже приводится обзор API AsyncResource.
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();
Конструкторы
AsyncResource(
type: string,
triggerAsyncId?: number | AsyncResourceOptions,
) AsyncResource() предназначен для расширения. Создание экземпляра AsyncResource() также вызывает init. Если triggerAsyncId не указан, используется async_hook.executionAsyncId().
Методы
asyncId(): number bind<Func extends (...args: any[]) => any>(fn: Func): Func Привязывает указанную функцию к области действия этого AsyncResource.
emitDestroy(): this Вызывает все хуки destroy. Этот метод следует вызывать только один раз. При повторном вызове будет выброшена ошибка. Его необходимо вызвать вручную. Если сборщик мусора удалит ресурс, хуки destroy вызваны не будут.
runInAsyncScope<
This,
Result,
>(
fn: (
this: This,
...args: any[],
) => Result,
thisArg?: This,
...args: any[],
): Result Вызывает переданную функцию с указанными аргументами в контексте выполнения асинхронного ресурса. Метод устанавливает контекст, запускает обратные вызовы AsyncHooks before, вызывает функцию, запускает обратные вызовы AsyncHooks after, а затем восстанавливает исходный контекст выполнения.
triggerAsyncId(): number Статические методы
bind<
Func extends (
this: ThisArg,
...args: any[],
) => any,
ThisArg,
>(
fn: Func,
type?: string,
thisArg?: ThisArg,
): Func Привязывает указанную функцию к текущему контексту выполнения.
функция createHook
Использование в Deno
import { createHook } from "node:async_hooks";
createHook(callbacks: HookCallbacks): AsyncHookРеализация createHook представляет собой неработающую заглушку.
Регистрирует функции, вызываемые при различных событиях жизненного цикла каждой асинхронной операции.
Обратные вызовы init()/before()/after()/destroy() вызываются при соответствующих асинхронных событиях в течение жизненного цикла ресурса.
Все обратные вызовы необязательны. Например, если нужно отслеживать только очистку ресурсов, достаточно передать обратный вызов destroy. Подробная информация обо всех функциях, которые можно передать в callbacks, приведена в разделе Hook Callbacks.
import { createHook } from 'node:async_hooks';
const asyncHook = createHook({
init(asyncId, type, triggerAsyncId, resource) { },
destroy(asyncId) { },
});
Обратные вызовы наследуются по цепочке прототипов:
class MyAsyncCallbacks {
init(asyncId, type, triggerAsyncId, resource) { }
destroy(asyncId) {}
}
class MyAddedCallbacks extends MyAsyncCallbacks {
before(asyncId) { }
after(asyncId) { }
}
const asyncHook = async_hooks.createHook(new MyAddedCallbacks());
Поскольку промисы являются асинхронными ресурсами, жизненный цикл которых отслеживается механизмом async hooks, обратные вызовы init(), before(), after() и destroy() не должны быть асинхронными функциями, возвращающими промисы.
Параметры
callbacks: HookCallbacks Hook Callbacks для регистрации
Возвращаемый тип
Экземпляр, используемый для отключения и включения хуков
функция executionAsyncId
Использование в Deno
import { executionAsyncId } from "node:async_hooks";
executionAsyncId(): numberРеализация executionAsyncId представляет собой неработающую заглушку.
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()
});
Идентификатор, возвращаемый executionAsyncId(), связан со временем выполнения, а не с причинно-следственными связями (которые описывает triggerAsyncId()):
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();
});
По умолчанию контексты промисов могут не получать точные executionAsyncIds. См. раздел об отслеживании выполнения промисов.
Возвращаемый тип
number asyncId текущего контекста выполнения. Полезен для отслеживания момента вызова.
функция executionAsyncResource
Использование в Deno
import { executionAsyncResource } from "node:async_hooks";
executionAsyncResource(): objectОбъекты ресурсов, возвращаемые executionAsyncResource(), чаще всего являются внутренними объектами дескрипторов Node.js с недокументированными API. Использование любых функций или свойств таких объектов может привести к сбою приложения, поэтому этого следует избегать.
При вызове executionAsyncResource() в контексте выполнения верхнего уровня возвращается пустой объект, поскольку нет доступного объекта дескриптора или запроса. Однако объект, представляющий верхний уровень, может быть полезен.
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
});
Это можно использовать для реализации локального хранилища продолжения без отслеживающего Map для хранения метаданных:
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);
Возвращаемый тип
object Ресурс, представляющий текущее выполнение. Полезен для хранения данных в ресурсе.
функция triggerAsyncId
Использование в Deno
import { triggerAsyncId } from "node:async_hooks";
triggerAsyncId(): numberconst 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();
});
По умолчанию контексты промисов могут не получать допустимые triggerAsyncId. См. раздел об отслеживании выполнения промисов.
Возвращаемый тип
number Идентификатор ресурса, ответственного за вызов выполняемого в данный момент обратного вызова.
интерфейс AsyncHook
Использование в Deno
import { type AsyncHook } from "node:async_hooks";
Методы
enable(): this Включает обратные вызовы указанного экземпляра AsyncHook. Если обратные вызовы не заданы, включение ничего не делает.
disable(): this Отключает обратные вызовы указанного экземпляра AsyncHook в общем пуле обратных вызовов AsyncHook, предназначенных для выполнения. После отключения хук не будет вызываться, пока его снова не включат.
интерфейс AsyncResourceOptions
Использование в Deno
import { type AsyncResourceOptions } from "node:async_hooks";
Свойства
triggerAsyncId: number | undefined Идентификатор контекста выполнения, создавшего это асинхронное событие.
requireManualDestroy: boolean | undefined Отключает автоматический вызов emitDestroy при сборке мусора для объекта. Обычно этот параметр не требуется задавать (даже если emitDestroy вызывается вручную), кроме случаев, когда извлекается asyncId ресурса и вместе с ним вызывается emitDestroy чувствительного API.
интерфейс HookCallbacks
Использование в Deno
import { type HookCallbacks } from "node:async_hooks";
Методы
init(
asyncId: number,
type: string,
triggerAsyncId: number,
resource: object,
): void Вызывается при создании экземпляра класса, который может инициировать асинхронное событие.
before(asyncId: number): void При запуске асинхронной операции или ее завершении вызывается обратный вызов, уведомляющий пользователя. Обратный вызов before вызывается непосредственно перед указанным обратным вызовом.
after(asyncId: number): void Вызывается сразу после завершения обратного вызова, указанного в before.
Если во время выполнения обратного вызова возникает необработанное исключение, after запускается после отправки события 'uncaughtException' или выполнения обработчика domain.
promiseResolve(asyncId: number): void Вызывается при вызове resolve() для промиса. Это может происходить не в том же идентификаторе выполнения, что и сам промис.
destroy(asyncId: number): void Вызывается после уничтожения ресурса, соответствующего asyncId
© 2018–2025 the Deno authors
Licensed under the MIT License.
https://docs.deno.com/api/node/async_hooks