Skip to solution
mediumLow-Level Design

What are 'Event Emitting' patterns and how does error-first callback convention relate to them?

1.2k views
01

Understand the problem

Question presented to candidate: "An operation can either take an error-first callback, or be modeled as an EventEmitter firing 'data'/'error' events. If the caller forgets to handle a failure in each style, does the same thing happen both times?"

What a strong answer should cover:

  • Error-first callback conventionfn(...args, (err, result) => {}) — and EventEmitter-based patterns.emit("data", result) / .emit("error", err) — are Node's two standard idioms for delivering an async result: one single callback invocation for a one-shot operation, versus multiple, ongoing events for something that can fire more than once (a stream's repeated 'data' events, a socket's connection lifecycle).
  • 📌 The critical, verified behavioral difference between the two, when a failure is not handled: a callback invoked with an Error that the caller's own callback body simply ignores produces no crash at all — verified directly, the error is silently swallowed, and the script continues normally. Emitting 'error' on an EventEmitter with no listener attached, by contrast, throws synchronously — verified directly as a real, caught exception — and, left uncaught further up, crashes the process.
  • This is not a minor implementation detail — it is a deliberate design choice: error-first callbacks put the burden of checking err entirely on the caller, with no enforcement; EventEmitter's 'error' special case (covered fully, with its own live verification, in the dedicated EventEmitter-basics question) makes ignoring a failure impossible to do silently — it becomes a loud crash instead.
  • The choice between the two patterns in your own API design should track the shape of the result: a single, one-time outcome (reading a file once) fits an error-first callback (or, in modern code, a Promise) naturally; a stream of ongoing events (a socket receiving many messages over its lifetime, a long-running watcher) fits EventEmitter naturally.
  • A precise answer connects this to Node's broader evolution: Promises/async-await (covered in their own dedicated questions) have largely superseded error-first callbacks for single-result async operations in modern code, while EventEmitter remains the standard, unreplaced idiom for ongoing, multi-event sources precisely because Promises only ever resolve/reject once.
  • The takeaway worth stating explicitly: silently ignorable errors are a real, structural risk of the error-first callback pattern specifically — a caller who writes fn(() => {}), discarding the err parameter entirely, introduces no visible symptom until the swallowed failure causes a much harder-to-trace problem downstream.

Clarifying questions expected:

  • "Is the operation genuinely one-shot, or does it produce results/events repeatedly over time?" — the deciding factor for which pattern actually fits.
  • "Is the concern designing a new API, or understanding why an existing one behaves the way it does on a missed error?"

Code / implementation expected: Yes — the actual, verified contrast between a silently-ignored callback error (no crash) and an unhandled 'error' emit (a real synchronous throw) is the concrete, convincing deliverable.

asyncpatternsevents
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 interviews — assumes familiarity with both EventEmitter basics and error-first callbacks individually, connecting the two. Difficulty: Medium

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

The same 'unhandled error' situation in both patterns — a callback silently swallows it, EventEmitter throws synchronously
const EventEmitter = require("events");

// Error-first callback: the caller simply ignores the err parameter
function op(cb) { cb(new Error("boom")); }
op(() => {}); // NO crash, no warning at all
console.log("callback style: error silently ignored, script continues");

// EventEmitter: emit("error") with NO listener attached
const e = new EventEmitter();
try {
  e.emit("error", new Error("boom"));
} catch (err) {
  console.log("EventEmitter style: emit(error) with no listener THROWS:", err.message);
}

// Output:
// callback style: error silently ignored, no crash, script continues
// EventEmitter style: emit(error) with no listener THROWS: boom
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 42 of 152 decoded in the Node.js track. One more won't hurt.

Back to track