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(fromnode: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
requestIdthroughout — including inside a real, separately-defined nested function several calls deep that received norequestIdparameter 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 viaals.getStore()— code running outside that specificrun()call, or in an unrelated concurrent chain, genuinely cannot see it. - The precise mechanism behind the prompt's concurrency guarantee:
AsyncLocalStorageis built on Node's own async-context tracking, which follows the actual causal chain of async operations — asetTimeout, aPromisecontinuation, or any nested async call within onerun()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
AsyncLocalStorageexists 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.