Skip to solution
hardBackend

What is AsyncLocalStorage and what problem does it solve?

335 views
01

Understand the problem

Question presented to candidate: "You need a request ID available inside a deeply nested logging function, several async calls deep, without manually passing it as a parameter through every single function in between. Under real concurrent load — two requests being handled at the same time — how do you guarantee one request's logs never accidentally show the other request's ID?"

What a strong answer should cover:

  • AsyncLocalStorage (from node:async_hooks) lets you store a value that's automatically, implicitly available to every function called within a given async execution chain — including deeply nested ones — with no parameter-threading required at all, directly answering the prompt's first requirement.
  • 📌 Verified, not assumed — the exact answer to the prompt's concurrency concern: two real, genuinely concurrent "requests" (running interleaved — the shorter-delay one finished before the longer one, confirmed directly) each correctly saw their own requestId throughout — including inside a real, separately-defined nested function several calls deep that received no requestId parameter at all — their contexts never crossed, even while genuinely running at the same time.
  • 📌 Interview term: als.run(store, callback) — the real mechanism that establishes a context: any code running inside that callback (and anything it calls, including asynchronously, verified above) can read the store via als.getStore() — code running outside that specific run() call, or in an unrelated concurrent chain, genuinely cannot see it.
  • The precise mechanism behind the prompt's concurrency guarantee: AsyncLocalStorage is built on Node's own async-context tracking, which follows the actual causal chain of async operations — a setTimeout, a Promise continuation, or any nested async call within one run() invocation stays linked to that invocation's store, genuinely independent of whatever unrelated async work happens to be interleaved with it at the exact same wall-clock time, verified directly above.
  • A precise answer names the canonical real use case: HTTP request-scoped context — a request ID, a user ID, a trace ID — genuinely needed by logging/error-handling code buried deep inside a call stack (a database layer, a third-party library) that was never written to accept and forward that value as an explicit parameter, and often shouldn't be rewritten to, since threading one extra parameter through every intermediate function is exactly the tedious, error-prone plumbing AsyncLocalStorage exists to eliminate.

Clarifying questions expected:

  • "Does every layer of the codebase that needs this context (third-party middleware, a database client's logging) genuinely support or interoperate with AsyncLocalStorage correctly, or could a library's own async handling silently break the context chain?" — a real, worth-confirming edge case for less common async patterns.
  • "Is this context genuinely read-only once established for a request, or does it need to be mutated partway through a single request's handling?" — shapes whether a plain object store, mutated in place, is the right choice.

Code / implementation expected: Yes — two real, genuinely concurrent, interleaved async operations, each correctly retaining their own context including in a nested function several calls deep, is the concrete, convincing proof of exactly how the isolation works under real concurrency, not just sequential calls.

nodejsasync-hookscontextobservability
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for Node.js async-architecture interviews — assumes familiarity with the event-loop and Promise-fundamentals questions in this bank. Difficulty: Hard

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

A real AsyncLocalStorage demo: two genuinely concurrent, interleaved requests, contexts never crossing, even nested calls deep
const { AsyncLocalStorage } = require("async_hooks");
const als = new AsyncLocalStorage();

function logWithContext(msg) {
  const ctx = als.getStore();
  console.log(`[requestId=${ctx?.requestId}] ${msg}`);
}

async function handleRequest(requestId, delayMs) {
  await als.run({ requestId }, async () => {
    logWithContext("request started");
    await new Promise((r) => setTimeout(r, delayMs));
    logWithContext("after real async delay, context genuinely preserved");
    await doNestedWork(); // no requestId parameter passed through at all
  });
}

async function doNestedWork() {
  logWithContext("nested function, several calls deep, still sees the right context");
}

Promise.all([
  handleRequest("A-111", 50),
  handleRequest("B-222", 20), // genuinely finishes first
]).then(() => console.log("both real concurrent requests finished, contexts never crossed"));

// [requestId=A-111] request started
// [requestId=B-222] request started
// [requestId=B-222] after real async delay, context genuinely preserved   <- B finishes first
// [requestId=B-222] nested function, several calls deep, still sees the right context
// [requestId=A-111] after real async delay, context genuinely preserved
// [requestId=A-111] nested function, several calls deep, still sees the right context
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 146 of 152 decoded in the Node.js track. One more won't hurt.

Back to track