Node.js Cheatsheet

Events

Use this Node.js reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

EventEmitter Basics

import { EventEmitter } from "node:events";

const emitter = new EventEmitter();

// Register listener
emitter.on("data", (chunk) => console.log("got:", chunk));

// One-time listener
emitter.once("connect", () => console.log("connected!"));

// Emit event
emitter.emit("data", "hello");
emitter.emit("data", "world");
emitter.emit("connect");   // fires listener, then removes it

// Emit with multiple args
emitter.emit("error", new Error("oops"), { context: "db" });

Registering and Removing Listeners

const handler = (msg) => console.log(msg);

// Add listeners
emitter.on("msg",      handler);         // alias: addListener
emitter.once("msg",    handler);         // once only
emitter.prependListener("msg", handler); // insert at front of queue
emitter.prependOnceListener("msg", handler);

// Remove
emitter.off("msg", handler);            // alias: removeListener
emitter.removeAllListeners("msg");      // remove all for event
emitter.removeAllListeners();           // remove ALL listeners

// Inspect
emitter.listeners("msg");               // copy of listener array
emitter.rawListeners("msg");            // includes once wrappers
emitter.listenerCount("msg");           // number of listeners
emitter.eventNames();                   // ["msg", "connect", ...]

Extending EventEmitter

import { EventEmitter } from "node:events";

class Database extends EventEmitter {
  constructor(url) {
    super();
    this.url = url;
  }

  async connect() {
    // ... connect logic ...
    this.emit("connect", this.url);
  }

  async query(sql) {
    this.emit("query", sql);
    // ... run query ...
    this.emit("result", rows);
    return rows;
  }
}

const db = new Database("postgres://...");
db.on("connect", (url) => console.log("connected to", url));
db.on("query",   (sql) => console.log("SQL:", sql));
await db.connect();

The error Event

// Unhandled 'error' events crash the process
emitter.on("error", (err) => {
  console.error("Emitter error:", err.message);
});

// If no error listener is attached, Node throws
emitter.emit("error", new Error("boom")); // uncaught if no listener!

// Safe pattern: always add an error listener
function makeEmitter() {
  const e = new EventEmitter();
  e.on("error", console.error);  // minimum safe fallback
  return e;
}

Max Listeners Warning

// Default limit is 10 — hit it and Node warns about a potential memory leak
emitter.setMaxListeners(20);       // raise for this instance
emitter.setMaxListeners(0);        // 0 = unlimited (disable warning)
emitter.getMaxListeners();         // current limit

// Global default (affects all new EventEmitters)
EventEmitter.defaultMaxListeners = 20;

events Module Utilities

import {
  EventEmitter,
  once,          // Promise that resolves on first emit
  on,            // Async iterable over all emits
  getEventListeners,
  setMaxListeners,
  addAbortListener,
} from "node:events";

// once — await a single event
const [data] = await once(emitter, "data");

// once with timeout / cancellation
const ac = new AbortController();
setTimeout(() => ac.abort(), 5000);
try {
  const [data] = await once(emitter, "data", { signal: ac.signal });
} catch (err) {
  if (err.name === "AbortError") console.log("timed out");
}

// on — async iterator over repeated events
for await (const [msg] of on(emitter, "message")) {
  console.log(msg);
  // Break when done to clean up
}

// Inspect listeners on any emitter
getEventListeners(emitter, "data");  // returns array

// Set max on multiple emitters at once
setMaxListeners(50, emitter1, emitter2, process);

EventTarget (Web-Compatible API)

// Node also supports the Web EventTarget API
const target = new EventTarget();

const handler = (event) => console.log(event.type, event.detail);

target.addEventListener("click", handler);
target.dispatchEvent(new Event("click"));
target.removeEventListener("click", handler);

// Custom events with detail
target.addEventListener("ping", (e) => console.log(e.detail));
target.dispatchEvent(new CustomEvent("ping", { detail: { ts: Date.now() } }));

process, Worker, and many Node stream objects implement both EventEmitter and EventTarget.

Listener Execution Order and Edge Cases

// Listeners fire in REGISTRATION ORDER
emitter.on("e", () => console.log("first"));
emitter.on("e", () => console.log("second"));
emitter.emit("e");  // first → second

// prependListener reverses one entry
emitter.prependListener("e", () => console.log("zero-th"));
emitter.emit("e");  // zero-th → first → second

// once listener is removed BEFORE the callback runs
emitter.once("e", function() {
  emitter.listenerCount("e");  // 0 — already removed
});

// emit is synchronous — all listeners run before next line
let done = false;
emitter.on("sync", () => { done = true; });
emitter.emit("sync");
console.log(done);  // true

// Modifying listeners DURING emit is safe (copy is made)
emitter.on("e", () => {
  emitter.removeAllListeners("e");  // won't affect current emit
});

Async Patterns with Events

// Convert callback-based event stream to promise
function waitForEvent(emitter, event, errorEvent = "error") {
  return new Promise((resolve, reject) => {
    function onEvent(...args) {
      emitter.off(errorEvent, onError);
      resolve(args);
    }
    function onError(err) {
      emitter.off(event, onEvent);
      reject(err);
    }
    emitter.once(event, onEvent);
    emitter.once(errorEvent, onError);
  });
}

// Collect all events until 'end'
async function collect(emitter) {
  const results = [];
  for await (const [item] of on(emitter, "data")) {
    results.push(item);
    if (results.length >= 100) break;  // guard against infinite stream
  }
  return results;
}