Как связаны node:async_hooks и AsyncLocalStorage?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Связь между node:async_hooks и AsyncLocalStorage
AsyncLocalStorage и модуль async_hooks — это две стороны одной медали в Node.js для управления асинхронным контекстом. Понимание их взаимосвязи критически важно для правильной работы с контекстной информацией в распределённых системах.
Что такое async_hooks?
async_hooks — это низкоуровневый API Node.js, который позволяет отслеживать жизненный цикл асинхронных ресурсов (таймеры, промисы, сокеты и т.д.). Он предоставляет хуки для перехвата:
- init — создание асинхронного ресурса
- before — начало выполнения колбэка ресурса
- after — завершение выполнения колбэка
- destroy — уничтожение ресурса
const async_hooks = require("async_hooks");
const hook = async_hooks.createHook({
init(asyncId, type, triggerAsyncId) {
console.log(`Ресурс ${type} создан с ID ${asyncId}`);
},
before(asyncId) {
console.log(`Выполнение ресурса ${asyncId}`);
},
after(asyncId) {
console.log(`Завершение ресурса ${asyncId}`);
},
});
hook.enable();
AsyncLocalStorage — высокоуровневая абстракция
AsyncLocalStorage построен на основе async_hooks и предоставляет простой интерфейс для хранения данных, изолированных в пределах одной цепочки выполнения:
const { AsyncLocalStorage } = require("async_hooks");
const userStorage = new AsyncLocalStorage();
// Установить значение для текущей цепочки
userStorage.run({ userId: 123 }, () => {
console.log(userStorage.getStore()); // { userId: 123 }
// Значение доступно даже в асинхронных операциях
setTimeout(() => {
console.log(userStorage.getStore()); // { userId: 123 }
}, 100);
});
console.log(userStorage.getStore()); // undefined — контекст потерян
Как они взаимосвязаны?
-
AsyncLocalStorage использует async_hooks внутри — когда вы вызываете
.run(), AsyncLocalStorage регистрирует хуки для отслеживания асинхронных ресурсов -
Автоматическое распространение контекста — благодаря async_hooks, данные, установленные в
.run(), автоматически доступны во всех асинхронных операциях внутри этой цепочки -
Изоляция контекста — каждая цепочка имеет свой контекст, что крайне важно для многопользовательского сервера
Практический пример: логирование с контекстом
const { AsyncLocalStorage } = require("async_hooks");
const express = require("express");
const requestContext = new AsyncLocalStorage();
const app = express();
app.use((req, res, next) => {
requestContext.run({ requestId: req.id, userId: req.user?.id }, () => {
next();
});
});
function log(message) {
const context = requestContext.getStore();
console.log(`[${context?.requestId}] ${message}`);
}
app.get("/api/users/:id", async (req, res) => {
log("Начало обработки запроса");
// Данные контекста сохраняются во всех асинхронных операциях
const user = await db.findUser(req.params.id);
log("Пользователь найден");
res.json(user);
});
Когда использовать async_hooks напрямую?
Напрямую async_hooks нужен для:
- Отладки и профилирования асинхронного кода
- Отслеживания утечек ресурсов
- Детального анализа performance
Для обычной работы с контекстом всегда используй AsyncLocalStorage — это безопаснее и проще.