← thecodex.expert · The Codex Family of Knowledge
The Codex / JavaScript / events — EventEmitter

events — EventEmitter

The backbone of Node.js's async model — EventEmitter, on/off/emit/once, error events, and async event patterns.

What it is, in plain English: EventEmitter is the foundation of Node.js's event-driven architecture. Almost everything in Node.js (streams, HTTP servers, file watchers) extends EventEmitter. You call emitter.on(event, listener) to subscribe, emitter.emit(event, ...args) to publish, and emitter.off(event, listener) to unsubscribe. The 'error' event is special — if emitted with no listener, Node.js crashes.
Where this bites you

emit() is synchronous: it calls each listener in turn, right there, before returning. If one listener throws, the error flies out of emit() into the code that emitted it, and the listeners after it never run. One failing email listener means the stock never gets updated, and the code that placed the order sees an exception it knows nothing about.

import { EventEmitter } from "events";

const orders = new EventEmitter();
orders.on("placed", (order) => {
  throw new Error(`email service down for order ${order.id}`);   // one listener fails...
});
orders.on("placed", (order) => {
  console.log("stock updated for", order.id);                     // ...so this one never runs
});

try {
  orders.emit("placed", { id: 42 });
  console.log("order saved");
} catch (e) {
  console.log("emit threw:", e.message);
}
// emit threw: email service down for order 42

Publishing and subscribing to events

import { EventEmitter } from "events";

const emitter = new EventEmitter();

// Subscribe (.on — fires every time):
emitter.on("data", (payload) => {
  console.log("Received:", payload);
});

// Subscribe once (.once — fires only the first time):
emitter.once("connect", () => {
  console.log("Connected! (fires once only)");
});

// Publish (emit an event):
emitter.emit("connect");              // "Connected! (fires once only)"
emitter.emit("connect");              // nothing — listener removed after first call
emitter.emit("data", { id: 1 });     // "Received: { id: 1 }"
emitter.emit("data", { id: 2 });     // "Received: { id: 2 }"

// Unsubscribe:
function handler(data) { console.log(data); }
emitter.on("event", handler);
emitter.off("event", handler);        // alias: removeListener()

Listeners are independent subscribers, so make each one responsible for its own failures. Catch inside the listener and report it, for example through the emitter's "error" event, so emit() carries on to the next listener and returns normally:

import { EventEmitter } from "events";

const orders = new EventEmitter();
orders.on("error", (e) => console.log("logged:", e.message));

orders.on("placed", (order) => {
  try {
    throw new Error(`email service down for order ${order.id}`);
  } catch (e) {
    orders.emit("error", e);           // report it; don't let it escape into emit()
  }
});
orders.on("placed", (order) => {
  console.log("stock updated for", order.id);
});

orders.emit("placed", { id: 42 });
console.log("order saved");
// logged: email service down for order 42
// stock updated for 42
// order saved

Extending EventEmitter

// Most Node.js built-ins extend EventEmitter:
import { EventEmitter } from "events";

class DatabaseConnection extends EventEmitter {
  async connect(url) {
    try {
      // ... connect to db ...
      this.emit("connect", { url, status: "connected" });
    } catch (e) {
      this.emit("error", e);  // ALWAYS handle 'error' event
    }
  }

  async query(sql) {
    const rows = []; // ... execute query ...
    this.emit("query", { sql, rowCount: rows.length });
    return rows;
  }

  close() {
    // ... close connection ...
    this.emit("close");
  }
}

const db = new DatabaseConnection();
db.on("connect", ({ url }) => console.log("Connected to:", url));
db.on("error",   (e) => console.error("DB error:", e.message));  // REQUIRED
db.on("close",   () => console.log("Disconnected"));

The error event is special

// If 'error' is emitted with no listener → Node.js CRASHES:
const emitter = new EventEmitter();
emitter.emit("error", new Error("oops"));  // ← throws the Error itself ("Unhandled 'error' event"): crash!

// Always add an error listener:
emitter.on("error", (err) => {
  console.error("Emitter error:", err.message);
  // handle or log — don't let it crash your server
});
Try It Yourself

Official Sources

The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.