nodejs
async hooks
performance analysis
javascript
asynchronous programming

Performance of nodejs async hooks

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

async_hooks in Node.js is powerful, but it is not free. It tracks the lifecycle of asynchronous resources, which means extra bookkeeping on every relevant async operation. The result is that enabling hooks can add measurable overhead, especially in high-throughput servers or in code that creates large numbers of promises, timers, or I/O callbacks.

Why There Is Overhead

The async_hooks API observes events such as resource creation and callback execution. Each tracked async resource can trigger lifecycle callbacks such as these:

  • 'init'
  • 'before'
  • 'after'
  • 'destroy'

That means extra work per async resource, not just per request at the application level. If your program creates many short-lived async operations, the cost can accumulate quickly.

Minimal Example

javascript
1const async_hooks = require('async_hooks');
2
3const hook = async_hooks.createHook({
4  init(asyncId, type) {
5    // Intentionally light work
6  },
7  before(asyncId) {},
8  after(asyncId) {},
9  destroy(asyncId) {}
10});
11
12hook.enable();
13
14setTimeout(() => {
15  console.log('done');
16}, 10);

Even an almost-empty hook still causes Node.js to route lifecycle events through the hook machinery.

The Real Cost Depends on What Your Callbacks Do

The overhead is not just the existence of hooks. It is also what your hook callbacks perform.

Expensive patterns include:

  • logging synchronously from hook callbacks
  • allocating large tracking objects per async resource
  • storing lots of metadata in large maps
  • capturing stacks or deep diagnostic information on every init

So the performance question is really two questions:

  • what does enabling hooks cost?
  • what does your hook implementation cost on top of that?

Benchmark the Workload, Not the API in Isolation

A tiny microbenchmark can show relative cost, but the real answer depends on your application's async profile.

javascript
1const async_hooks = require('async_hooks');
2const iterations = 100000;
3
4function run(label) {
5  const start = process.hrtime.bigint();
6  let pending = iterations;
7
8  for (let i = 0; i < iterations; i++) {
9    Promise.resolve().then(() => {
10      pending--;
11      if (pending === 0) {
12        const end = process.hrtime.bigint();
13        console.log(label, Number(end - start) / 1e6, 'ms');
14      }
15    });
16  }
17}
18
19run('without hooks');

Then rerun with the hook enabled and compare. That will not give a universal truth, but it will tell you the cost on your current Node version and workload shape.

Prefer Higher-Level Tools When They Fit

If your goal is request-local context propagation, AsyncLocalStorage is often a better abstraction than manually managing raw async_hooks state. It still relies on the async context machinery underneath, but the API is easier to use correctly.

If your goal is deep diagnostics, keep the instrumentation as targeted as possible rather than turning on rich tracking globally for every environment.

Production Guidance

A reasonable rule is:

  • avoid enabling heavy hooks by default in hot production paths unless you measured the cost
  • keep hook callbacks minimal
  • use feature flags or diagnostic modes when possible
  • benchmark after every major instrumentation change

This is especially important for APIs, job workers, and services with very high promise churn.

Common Pitfalls

The biggest mistake is treating async_hooks overhead as a fixed constant independent of what the callbacks do.

Another mistake is writing logs or expensive diagnostics directly inside init, before, or after for every async resource.

A third issue is shipping hook-based instrumentation to production without benchmarking the actual workload.

Summary

  • 'async_hooks adds overhead because Node must track async resource lifecycles'
  • The performance cost depends heavily on how many async resources you create and what your hook callbacks do
  • Empty or lightweight hooks cost less than hooks that log, allocate heavily, or capture diagnostics
  • Benchmark on your real workload instead of relying on generic assumptions
  • Use higher-level abstractions such as AsyncLocalStorage when they match the use case better than raw hooks

Course illustration
Course illustration

All Rights Reserved.