Node.js Async Hook Abstraction
async-hook-jl is a Node.js library that provides a high-level, stable abstraction over Node.js's internal, currently undocumented `AsyncWrap` API. It enables developers to inspect and hook into the lifecycle of "handle objects" (internal resources like network connections, timers, etc.) within the Node.js runtime. This includes events like initialization (`init`), pre-execution (`pre`), post-execution (`post`), and destruction (`destroy`) of asynchronous operations. The library aims to address some inconsistencies in the native `AsyncWrap` API, offer a more uniform interface, and crucially, allow multiple hooks to be registered simultaneously. The current stable version is 1.7.6, with recent updates focusing on compatibility and minor fixes rather than new features. Its primary differentiator is making a powerful, low-level internal Node.js API accessible and more robust for userland modules, with the long-term hope that similar functionality will eventually be integrated directly into Node.js core.
Common errors
-
ReferenceError: asyncHooks is not defined
cause The module's main export is named `asyncHook` (singular), but the user might have mistakenly used `asyncHooks` (plural) based on a typo in the README's `removeHooks` example.fixChange `asyncHooks` to `asyncHook`. For example, `asyncHook.removeHooks(...)` instead of `asyncHooks.removeHooks(...)`. -
TypeError: asyncHook.addHooks is not a function
cause This typically occurs if `require('async-hook-jl')` failed to correctly load the module, or if `asyncHook` was reassigned or shadowed, or if the module was imported incorrectly in an ESM context.fixEnsure `const asyncHook = require('async-hook-jl');` is used at the top of your file. Verify that `async-hook-jl` is correctly installed in `node_modules` and that no other variable named `asyncHook` is conflicting. -
Hooks are registered but not firing (no console output from hook functions).
cause The `async-hook-jl` hooks are disabled by default and must be explicitly enabled using `asyncHook.enable()` after registration.fixEnsure you call `asyncHook.enable();` after calling `asyncHook.addHooks(...)` to activate the hook callbacks.
Warnings
- breaking `async-hook-jl` relies on Node.js's internal, undocumented `AsyncWrap` API. Future Node.js major or even minor versions might introduce breaking changes to `AsyncWrap` that could affect this library's functionality without prior notice, potentially requiring updates or causing unexpected runtime errors.
- gotcha Disabling hooks globally using `asyncHook.disable()` can lead to conflicts or unexpected behavior with other modules that might also be using `async-hook-jl` or directly interacting with `AsyncWrap`. This can break their functionality or lead to difficult-to-debug side effects.
- gotcha The package's README, specifically in the `removeHooks` example, contains a typo. It incorrectly uses `asyncHooks.removeHooks` (plural) instead of `asyncHook.removeHooks` (singular). Copying this code verbatim will result in a `ReferenceError`.
- gotcha The `async-hook-jl` module is primarily designed for CommonJS (`require`) environments. While Node.js itself supports ESM, directly importing this module using `import ... from 'async-hook-jl'` may not work as intended or could lead to compatibility issues depending on your Node.js version and configuration.
Install
-
npm install async-hook-jl -
yarn add async-hook-jl -
pnpm add async-hook-jl
Imports
- asyncHook
import asyncHook from 'async-hook-jl';
const asyncHook = require('async-hook-jl'); - addHooks
import { addHooks } from 'async-hook-jl';asyncHook.addHooks({ init, pre, post, destroy }); - enable
asyncHooks.enable();
asyncHook.enable();
Quickstart
const asyncHook = require('async-hook-jl');
function init(uid, handle, provider, parentUid, parentHandle) {
console.log(`[INIT] UID: ${uid}, Provider: ${asyncHook.providers[provider]}, Parent UID: ${parentUid}`);
}
function pre(uid, handle) {
console.log(`[PRE] UID: ${uid}`);
}
function post(uid, handle, didThrow) {
console.log(`[POST] UID: ${uid}, DidThrow: ${didThrow}`);
}
function destroy(uid) {
console.log(`[DESTROY] UID: ${uid}`);
}
// Add the defined hooks
asyncHook.addHooks({ init, pre, post, destroy });
// Enable the hooks globally
asyncHook.enable();
// Demonstrate an asynchronous operation
console.log('Starting timer...');
setTimeout(() => {
console.log('Timer finished after 100ms.');
}, 100);
// Optional: Disable after some time or specific operations
// setTimeout(() => {
// asyncHook.disable();
// console.log('Async hooks disabled.');
// }, 500);