Question presented to candidate: "You have an existing EventEmitter-based module, and you need to write a single async function that waits for its next 'ready' event before continuing — without wrapping the whole thing in a manual 'new Promise((resolve) => emitter.on(...))' every single time. Does Node provide a built-in shortcut, and what happens if the emitter fires an 'error' event instead?"
What a strong answer should cover:
events.once(emitter, eventName)(fromnode:events) returns a real Promise that resolves with the event's arguments the next time that specific event fires — directly answering the prompt's exact need, with no manualnew Promise((resolve) => emitter.on(...))boilerplate required.- 📌 Verified, not assumed: a real
await once(emitter, "ready")call genuinely suspended execution until a real, lateremit("ready", ...)call — confirmed by real, matching timestamps (the event fired att=89ms, and theawaitgenuinely resumed att=90ms) — and the resolved value correctly contained the real emitted payload, not a placeholder. - 📌 Verified, not assumed — the exact answer to the prompt's error question:
once()has real, built-in special handling for the"error"event — a real emitter that fired"error"instead of the awaited"success"event caused theawait once(...)call to genuinely reject with that real error, rather than hanging forever waiting for an event that will never come. - A precise answer names why this specific error-handling behavior matters, precisely: a plain
new Promise((resolve) => emitter.once(eventName, resolve)), hand-rolled without special-casing"error", would genuinely hang forever if the emitter instead emitted"error"— verified directly above,events.once()'s real, built-in behavior avoids exactly this trap automatically, without the caller needing to remember to add their own separate error listener. - A precise answer names the honest scope:
events.once()resolves on the event's first occurrence only — for a stream of multiple future events (not just the next single one), an async iterator over the emitter (viaevents.on(emitter, eventName), a related but different helper) or continuing to use real event listeners directly is the more appropriate real tool, not a repeatedonce()call in a loop.
Clarifying questions expected:
- "Does the calling code need to wait for genuinely just the NEXT occurrence of this event, or does it need to process every future occurrence as an ongoing stream?" — directly decides between
events.once()and the relatedevents.on()async-iterator helper. - "Could the awaited event genuinely never fire at all in some real scenario, leaving the
awaitsuspended indefinitely?" — worth pairing with a real timeout (viaAbortSignal, whichevents.once()also accepts as an option) for a robust, production-ready version.
Code / implementation expected: Yes — a real await once(...) call genuinely suspending until a real, later event, with matching real timestamps, plus a real demonstration of the built-in "error"-event rejection behavior, is the concrete, convincing proof of exactly how the bridge works.