Async hooks
Source Code: lib/async_hooks.js
The async_hooks модуль предоставляет API для отслеживания асинхронных ресурсов. К нему можно получить доступ с помощью:
MJS модулей
import async_hooks from 'async_hooks';
CJS модулей
const async_hooks = require('async_hooks'); Терминология
Асинхронный ресурс представляет собой объект с ассоциированным обратным вызовом. Этот обратный вызов может вызываться несколько раз, например, событие 'connection' в net.createServer(), или только один раз, как в fs.open(). Ресурс также может быть закрыт до вызова обратного вызова. AsyncHook не делает явного различия между этими случаями, но будет представлять их как абстрактное понятие — ресурс.
Если используются Worker, каждый поток имеет независимый интерфейс async_hooks, и каждый поток будет использовать новый набор идентификаторов async.
Обзор
Ниже представлен краткий обзор публичного API.
MJS модулей
import async_hooks from 'async_hooks';
// Return the ID of the current execution context.
const eid = async_hooks.executionAsyncId();
// Return the ID of the handle responsible for triggering the callback of the
// current execution scope to call.
const tid = async_hooks.triggerAsyncId();
// Create a new AsyncHook instance. All of these callbacks are optional.
const asyncHook =
async_hooks.createHook({ init, before, after, destroy, promiseResolve });
// Allow callbacks of this AsyncHook instance to call. This is not an implicit
// action after running the constructor, and must be explicitly run to begin
// executing callbacks.
asyncHook.enable();
// Disable listening for new asynchronous events.
asyncHook.disable();
//
// The following are the callbacks that can be passed to createHook().
//
// init is called during object construction. The resource may not have
// completed construction when this callback runs, therefore all fields of the
// resource referenced by "asyncId" may not have been populated.
function init(asyncId, type, triggerAsyncId, resource) { }
// Before is called just before the resource's callback is called. It can be
// called 0-N times for handles (such as TCPWrap), and will be called exactly 1
// time for requests (such as FSReqCallback).
function before(asyncId) { }
// After is called just after the resource's callback has finished.
function after(asyncId) { }
// Destroy is called when the resource is destroyed.
function destroy(asyncId) { }
// promiseResolve is called only for promise resources, when the
// `resolve` function passed to the `Promise` constructor is invoked
// (either directly or through other means of resolving a promise).
function promiseResolve(asyncId) { }
CJS модулей
const async_hooks = require('async_hooks');
// Return the ID of the current execution context.
const eid = async_hooks.executionAsyncId();
// Return the ID of the handle responsible for triggering the callback of the
// current execution scope to call.
const tid = async_hooks.triggerAsyncId();
// Create a new AsyncHook instance. All of these callbacks are optional.
const asyncHook =
async_hooks.createHook({ init, before, after, destroy, promiseResolve });
// Allow callbacks of this AsyncHook instance to call. This is not an implicit
// action after running the constructor, and must be explicitly run to begin
// executing callbacks.
asyncHook.enable();
// Disable listening for new asynchronous events.
asyncHook.disable();
//
// The following are the callbacks that can be passed to createHook().
//
// init is called during object construction. The resource may not have
// completed construction when this callback runs, therefore all fields of the
// resource referenced by "asyncId" may not have been populated.
function init(asyncId, type, triggerAsyncId, resource) { }
// Before is called just before the resource's callback is called. It can be
// called 0-N times for handles (such as TCPWrap), and will be called exactly 1
// time for requests (such as FSReqCallback).
function before(asyncId) { }
// After is called just after the resource's callback has finished.
function after(asyncId) { }
// Destroy is called when the resource is destroyed.
function destroy(asyncId) { }
// promiseResolve is called only for promise resources, when the
// `resolve` function passed to the `Promise` constructor is invoked
// (either directly or through other means of resolving a promise).
function promiseResolve(asyncId) { }
async_hooks.createHook(callbacks)
-
callbacks<Объект> Обратные вызовы хуков для регистрации - Возвращает: <AsyncHook> Экземпляр, используемый для отключения и включения хуков
Регистрирует функции, которые должны вызываться для различных событий жизненного цикла каждой асинхронной операции.
Обратные вызовы init()/before()/after()/destroy() вызываются для соответствующего асинхронного события в течение жизненного цикла ресурса.
Все обратные вызовы необязательны. Например, если необходимо только отслеживать очистку ресурсов, то необходимо передать только обратный вызов destroy. Подробная информация обо всех функциях, которые можно передать в callbacks, находится в разделе Обратные вызовы хуков.
MJS модулей
import { createHook } from 'async_hooks';
const asyncHook = createHook({
init(asyncId, type, triggerAsyncId, resource) { },
destroy(asyncId) { }
});
CJS модулей
const async_hooks = require('async_hooks');
const asyncHook = async_hooks.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() не должны быть асинхронными функциями, которые возвращают обещания.
Обработка ошибок
Если какой-либо обратный вызов AsyncHook выбросит ошибку, приложение выведет трассировку стека и завершится. Путь завершения аналогичен пути при возникновении необработанного исключения, но все слушатели 'uncaughtException' будут удалены, тем самым принуждая процесс к завершению. Обратные вызовы 'exit' всё ещё будут вызваны, если приложение не запущено с --abort-on-uncaught-exception, в этом случае будет выведена трассировка стека и приложение завершит работу, оставив файл core.
Причина такого поведения обработки ошибок заключается в том, что эти обратные вызовы выполняются в потенциально изменчивых точках в течение жизненного цикла объекта, например, во время создания и уничтожения класса. Поэтому считается необходимым быстро завершить процесс, чтобы предотвратить непреднамеренное прерывание в будущем. Это может быть изменено в будущем, если будет проведен всесторонний анализ для обеспечения того, что исключение может следовать стандартному потоку управления без непреднамеренных побочных эффектов.
Вывод в обратных вызовах AsyncHooks
Поскольку вывод в консоль — асинхронная операция, console.log() приведет к вызову обратных вызовов AsyncHooks. Использование console.log() или аналогичных асинхронных операций внутри функции обратного вызова AsyncHooks приведет к бесконечной рекурсии. Простым решением при отладке является использование синхронной операции записи в журнал, такой как fs.writeFileSync(file, msg, flag). Это будет выводить данные в файл и не вызовет рекурсивный вызов AsyncHooks, потому что это синхронная операция.
MJS модулей
import { writeFileSync } from 'fs';
import { format } from 'util';
function debug(...args) {
// Use a function like this one when debugging inside an AsyncHooks callback
writeFileSync('log.out', `${format(...args)}\n`, { flag: 'a' });
}
CJS модулей
const fs = require('fs');
const util = require('util');
function debug(...args) {
// Use a function like this one when debugging inside an AsyncHooks callback
fs.writeFileSync('log.out', `${util.format(...args)}\n`, { flag: 'a' });
} Если для записи в журнал требуется асинхронная операция, можно отслеживать причину асинхронной операции, используя информацию, предоставляемую самим AsyncHooks. Тогда запись в журнал должна быть пропущена, если запись в журнал сама вызвала вызов обратного вызова AsyncHooks. Таким образом, бесконечная рекурсия будет прервана.
Класс: AsyncHook
Класс AsyncHook предоставляет интерфейс для отслеживания событий жизненного цикла асинхронных операций.
asyncHook.enable()
- Возвращает: <AsyncHook> Ссылка на
asyncHook.
Включает обратные вызовы для данного экземпляра AsyncHook. Если обратные вызовы не предоставлены, включение является пустой операцией.
Экземпляр AsyncHook по умолчанию отключен. Если экземпляр AsyncHook нужно включить сразу после создания, можно использовать следующий шаблон.
Модули MJS
import { createHook } from 'async_hooks';
const hook = createHook(callbacks).enable();
Модули CJS
const async_hooks = require('async_hooks');
const hook = async_hooks.createHook(callbacks).enable();
asyncHook.disable()
- Возвращает: <AsyncHook> Ссылка на
asyncHook.
Отключает обратные вызовы для данного экземпляра AsyncHook из глобального пула обратных вызовов AsyncHook для выполнения. После отключения хук больше не будет вызываться, пока не будет включён.
Для согласованности API disable() также возвращает экземпляр AsyncHook.
Обратные вызовы хуков
Ключевые события в жизненном цикле асинхронных событий были разделены на четыре области: создание, до/после вызова обратного вызова и уничтожение экземпляра.
init(asyncId, type, triggerAsyncId, resource)
-
asyncId<число> Уникальный идентификатор асинхронного ресурса. -
type<строка> Тип асинхронного ресурса. -
triggerAsyncId<число> Уникальный идентификатор асинхронного ресурса, в контексте выполнения которого был создан этот асинхронный ресурс. -
resource<Объект> Ссылка на ресурс, представляющий асинхронную операцию, необходимо освободить во время destroy.
Вызывается при создании класса, который может генерировать асинхронное событие. Это не означает, что экземпляр должен вызвать before/after до вызова destroy, только что такая возможность существует.
Это поведение можно наблюдать, например, открыв ресурс, а затем закрыв его, прежде чем ресурс можно будет использовать. Следующий фрагмент демонстрирует это.
Модули MJS
import { createServer } from 'net';
createServer().listen(function() { this.close(); });
// OR
clearTimeout(setTimeout(() => {}, 10));
Модули CJS
require('net').createServer().listen(function() { this.close(); });
// OR
clearTimeout(setTimeout(() => {}, 10));
Каждый новый ресурс получает ID, уникальный в пределах текущего экземпляра Node.js.
type
type — это строка, определяющая тип ресурса, который вызвал init. Как правило, он соответствует имени конструктора ресурса.
FSEVENTWRAP, FSREQCALLBACK, GETADDRINFOREQWRAP, GETNAMEINFOREQWRAP, HTTPINCOMINGMESSAGE, HTTPCLIENTREQUEST, JSSTREAM, PIPECONNECTWRAP, PIPEWRAP, PROCESSWRAP, QUERYWRAP, SHUTDOWNWRAP, SIGNALWRAP, STATWATCHER, TCPCONNECTWRAP, TCPSERVERWRAP, TCPWRAP, TTYWRAP, UDPSENDWRAP, UDPWRAP, WRITEWRAP, ZLIB, SSLCONNECTION, PBKDF2REQUEST, RANDOMBYTESREQUEST, TLSWRAP, Microtask, Timeout, Immediate, TickObject
Также существует тип ресурса PROMISE, который используется для отслеживания экземпляров Promise и запланированной ими асинхронной работы.
Пользователи могут определять свои собственные типы type при использовании публичного API встраивания.
Возможны коллизии имён типов. Встраивающим рекомендуется использовать уникальные префиксы, такие как имя пакета npm, чтобы предотвратить коллизии при прослушивании хуков.
triggerAsyncId
triggerAsyncId — это asyncId ресурса, который вызвал (или «спровоцировал») инициализацию нового ресурса и заставил init вызвать. Это отличается от async_hooks.executionAsyncId(), которое показывает когда был создан ресурс, в то время как triggerAsyncId показывает почему был создан ресурс.
Следующее демонстрирует triggerAsyncId:
Модули MJS
import { createHook, executionAsyncId } from 'async_hooks';
import { stdout } from 'process';
import net from 'net';
createHook({
init(asyncId, type, triggerAsyncId) {
const eid = executionAsyncId();
fs.writeSync(
stdout.fd,
`${type}(${asyncId}): trigger: ${triggerAsyncId} execution: ${eid}\n`);
}
}).enable();
net.createServer((conn) => {}).listen(8080);
Модули CJS
const { createHook, executionAsyncId } = require('async_hooks');
const { stdout } = require('process');
const net = require('net');
createHook({
init(asyncId, type, triggerAsyncId) {
const eid = executionAsyncId();
fs.writeSync(
stdout.fd,
`${type}(${asyncId}): trigger: ${triggerAsyncId} execution: ${eid}\n`);
}
}).enable();
net.createServer((conn) => {}).listen(8080);
Вывод при обращении к серверу с nc localhost 8080:
TCPSERVERWRAP(5): trigger: 1 execution: 1 TCPWRAP(7): trigger: 5 execution: 0
TCPSERVERWRAP — это сервер, который получает подключения.
TCPWRAP — новое подключение от клиента. При установлении нового подключения экземпляр TCPWrap сразу же создается. Это происходит вне стека JavaScript. (Выполнение executionAsyncId() от 0 означает, что оно выполняется из C++ без стека JavaScript над ним). С этой информацией было бы невозможно связать ресурсы с точки зрения того, что вызвало их создание, поэтому triggerAsyncId получает задачу распространять информацию о том, какой ресурс отвечает за существование нового ресурса.
resource
resource — это объект, представляющий фактический асинхронный ресурс, который был инициализирован. Он может содержать полезную информацию, которая может меняться в зависимости от значения type Например, для типа ресурса GETADDRINFOREQWRAP resource предоставляет имя хоста, используемое при поиске IP-адреса хоста в net.Server.listen(). API для доступа к этой информации не поддерживается, но с помощью API встраивания пользователи могут предоставлять и документировать свои собственные объекты ресурсов. Например, такой объект ресурса может содержать выполняемый SQL-запрос.
В некоторых случаях объект ресурса повторно используется для повышения производительности, поэтому его небезопасно использовать в качестве ключа в WeakMap или добавлять к нему свойства.
Пример асинхронного контекста
Следующий пример содержит дополнительную информацию о вызовах init между вызовами before и after, в частности, о том, как будет выглядеть обратный вызов listen(). Форматирование вывода несколько более подробное для более удобного просмотра контекста вызова.
const { fd } = process.stdout;
let indent = 0;
async_hooks.createHook({
init(asyncId, type, triggerAsyncId) {
const eid = async_hooks.executionAsyncId();
const indentStr = ' '.repeat(indent);
fs.writeSync(
fd,
`${indentStr}${type}(${asyncId}):` +
` trigger: ${triggerAsyncId} execution: ${eid}\n`);
},
before(asyncId) {
const indentStr = ' '.repeat(indent);
fs.writeSync(fd, `${indentStr}before: ${asyncId}\n`);
indent += 2;
},
after(asyncId) {
indent -= 2;
const indentStr = ' '.repeat(indent);
fs.writeSync(fd, `${indentStr}after: ${asyncId}\n`);
},
destroy(asyncId) {
const indentStr = ' '.repeat(indent);
fs.writeSync(fd, `${indentStr}destroy: ${asyncId}\n`);
},
}).enable();
net.createServer(() => {}).listen(8080, () => {
// Let's wait 10ms before logging the server started.
setTimeout(() => {
console.log('>>>', async_hooks.executionAsyncId());
}, 10);
});
Вывод только при запуске сервера:
TCPSERVERWRAP(5): trigger: 1 execution: 1 TickObject(6): trigger: 5 execution: 1 before: 6 Timeout(7): trigger: 6 execution: 6 after: 6 destroy: 6 before: 7 >>> 7 TickObject(8): trigger: 7 execution: 7 after: 7 before: 8 after: 8
Как показано в примере, executionAsyncId() и execution каждый указывают значение текущего контекста выполнения; которые разграничены вызовами before и after.
Использование только execution для построения графика распределения ресурсов приводит к следующему:
root(1)
^
|
TickObject(6)
^
|
Timeout(7)
TCPSERVERWRAP не входит в этот график, хотя он и послужил причиной вызова console.log(). Это происходит потому, что привязка к порту без имени хоста является синхронной операцией, но для поддержания полностью асинхронного API обратный вызов пользователя помещается в process.nextTick(). Именно поэтому TickObject присутствует в выводе и является «родительским» для обратного вызова .listen().
График показывает только когда был создан ресурс, а не почему, поэтому для отслеживания почему используйте triggerAsyncId. Что можно представить следующим графиком:
bootstrap(1)
|
˅
TCPSERVERWRAP(5)
|
˅
TickObject(6)
|
˅
Timeout(7)
before(asyncId)
-
asyncId<число>
При инициации асинхронной операции (например, при получении нового подключения TCP-сервером) или её завершении (например, при записи данных на диск) вызывается обратный вызов для уведомления пользователя. Обратный вызов before вызывается непосредственно перед выполнением указанного обратного вызова. asyncId — это уникальный идентификатор, назначенный ресурсу, который собирается выполнить обратный вызов.
Обратный вызов before будет вызван от 0 до N раз. Обратный вызов before обычно вызывается 0 раз, если асинхронная операция была отменена или, например, если TCP-сервер не получил подключений. Персистентные асинхронные ресурсы, такие как TCP-сервер, обычно вызывают обратный вызов before несколько раз, в то время как другие операции, такие как fs.open() вызывают его только один раз.
after(asyncId)
-
asyncId<число>
Вызывается сразу после завершения обратного вызова, указанного в before.
Если при выполнении обратного вызова возникает непредвиденная ошибка, то after выполнится после события 'uncaughtException' или обработчик domain.
destroy(asyncId)
-
asyncId<число>
Вызывается после уничтожения ресурса, соответствующего asyncId. Также вызывается асинхронно из API встраивания emitDestroy().
Некоторые ресурсы зависят от сбора мусора для очистки, поэтому если на объект resource, переданный в init, есть ссылка, то destroy может никогда не быть вызван, что приведёт к утечке памяти в приложении. Если ресурс не зависит от сбора мусора, то это не проблема.
promiseResolve(asyncId)
-
asyncId<число>
Вызывается, когда функция resolve, переданная в конструктор Promise, вызывается (либо напрямую, либо другими способами разрешения обещания).
resolve() не выполняет никаких наблюдаемых синхронных операций.
Обещание Promise не обязательно выполнено или отклонено на этом этапе, если Promise было разрешено путём предположения состояния другого Promise.
new Promise((resolve) => resolve(true)).then((a) => {});
вызывает следующие обратные вызовы:
init for PROMISE with id 5, trigger id: 1 promise resolve 5 # corresponds to resolve(true) init for PROMISE with id 6, trigger id: 5 # the Promise returned by then() before 6 # the then() callback is entered promise resolve 6 # the then() callback resolves the promise by returning after 6
async_hooks.executionAsyncResource()
- Возвращает: <Объект> Ресурс, представляющий текущее выполнение. Полезно для хранения данных в ресурсе.
Объекты ресурсов, возвращаемые executionAsyncResource(), чаще всего являются внутренними объектами обработки Node.js с недокументированным API. Использование каких-либо функций или свойств объекта может привести к сбою приложения и должно быть избегаемо.
Использование executionAsyncResource() в контексте выполнения верхнего уровня вернёт пустой объект, так как нет объекта-обработчика или запроса для использования, но наличие объекта, представляющего верхний уровень, может быть полезным.
Модули MJS
import { open } from 'fs';
import { executionAsyncId, executionAsyncResource } from 'async_hooks';
console.log(executionAsyncId(), executionAsyncResource()); // 1 {}
open(new URL(import.meta.url), 'r', (err, fd) => {
console.log(executionAsyncId(), executionAsyncResource()); // 7 FSReqWrap
});
Модули CJS
const { open } = require('fs');
const { executionAsyncId, executionAsyncResource } = require('async_hooks');
console.log(executionAsyncId(), executionAsyncResource()); // 1 {}
open(__filename, 'r', (err, fd) => {
console.log(executionAsyncId(), executionAsyncResource()); // 7 FSReqWrap
}); Это можно использовать для реализации локального хранения продолжения без использования отслеживания Map для хранения метаданных:
Модули MJS
import { createServer } from 'http';
import {
executionAsyncId,
executionAsyncResource,
createHook
} from '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);
Модули CJS
const { createServer } = require('http');
const {
executionAsyncId,
executionAsyncResource,
createHook
} = require('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);
async_hooks.executionAsyncId()
- Возвращает: <число> Идентификатор текущего контекста выполнения. Полезно для отслеживания, когда что-то вызывается.
Модули MJS
import { executionAsyncId } from 'async_hooks';
console.log(executionAsyncId()); // 1 - bootstrap
fs.open(path, 'r', (err, fd) => {
console.log(executionAsyncId()); // 6 - open()
});
Модули CJS
const async_hooks = require('async_hooks');
console.log(async_hooks.executionAsyncId()); // 1 - bootstrap
fs.open(path, 'r', (err, fd) => {
console.log(async_hooks.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. См. раздел отслеживание выполнения промисов.
async_hooks.triggerAsyncId()
- Возвращает: <число> Идентификатор ресурса, ответственного за вызов обратного вызова, который в данный момент выполняется.
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();
}); Контексты промисов по умолчанию могут не получить корректные идентификаторы triggerAsyncId. См. раздел отслеживание выполнения промисов.
Отслеживание выполнения промисов
По умолчанию выполнения промисов не присваиваются идентификаторы asyncId из-за относительно высокой стоимости API интроспекции промисов, предоставляемого V8. Это означает, что программы, использующие промисы или async/await по умолчанию не получат правильных идентификаторов выполнения и вызова для контекстов обратных вызовов промисов.
Модули MJS
import { executionAsyncId, triggerAsyncId } from 'async_hooks';
Promise.resolve(1729).then(() => {
console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`);
});
// produces:
// eid 1 tid 0
Модули CJS
const { executionAsyncId, triggerAsyncId } = require('async_hooks');
Promise.resolve(1729).then(() => {
console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`);
});
// produces:
// eid 1 tid 0 Обратите внимание, что обратный вызов then() утверждает, что был выполнен в контексте внешнего области видимости, даже несмотря на то, что был асинхронный переход. Также значение triggerAsyncId равно 0, что означает, что нам не хватает контекста о ресурсе, вызвавшем (сделавшем запрос) выполнение обратного вызова then().
Установка async hooks через async_hooks.createHook включает отслеживание выполнения промисов:
Модули MJS
import { createHook, executionAsyncId, triggerAsyncId } from 'async_hooks';
createHook({ init() {} }).enable(); // forces PromiseHooks to be enabled.
Promise.resolve(1729).then(() => {
console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`);
});
// produces:
// eid 7 tid 6
Модули CJS
const { createHook, exectionAsyncId, triggerAsyncId } = require('async_hooks');
createHook({ init() {} }).enable(); // forces PromiseHooks to be enabled.
Promise.resolve(1729).then(() => {
console.log(`eid ${executionAsyncId()} tid ${triggerAsyncId()}`);
});
// produces:
// eid 7 tid 6 В этом примере добавление любой функции-обработчика включило отслеживание промисов. В примере выше есть два промиса; промис, созданный Promise.resolve(), и промис, возвращаемый вызовом к then(). В примере выше первый промис получил идентификатор asyncId 6, а второй — asyncId 7. Во время выполнения обратного вызова then(), мы выполняем в контексте промиса с asyncId 7. Этот промис был вызван асинхронным ресурсом 6.
Ещё одной особенностью промисов является то, что обратные вызовы before и after выполняются только на цепочке промисов. Это означает, что промисы, не созданные then()/catch(), не будут иметь обратных вызовов before и after.
API встраивателя JavaScript
Разработчики библиотек, которые сами обрабатывают асинхронные ресурсы, выполняя задачи, такие как ввод-вывод, пулы соединений или управление очередями обратных вызовов, могут использовать API AsyncResource JavaScript, чтобы вызывались все необходимые обратные вызовы.
Класс: AsyncResource
Документация по этому классу переехала в AsyncResource.
Класс: AsyncLocalStorage
Документация по этому классу переехала в AsyncLocalStorage.
© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/dist/latest-v16.x/docs/api/async_hooks.html