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 bothEventEmitterandEventTarget.
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; }