# Events and EventEmitter

## 1. The Core Pattern

`EventEmitter` (from the built-in `events` module) is the foundation Node's own APIs are built on — streams, `http.Server`, `child_process`, `process` itself all extend or use it. It implements the classic observer pattern: objects emit **named events**, and any number of listener functions can subscribe to react when that event fires.

```js
const EventEmitter = require("events");

const emitter = new EventEmitter();

emitter.on("greet", (name) => {
  console.log(`Hello, ${name}!`);
});

emitter.emit("greet", "Ana"); // "Hello, Ana!" — runs listener(s) SYNCHRONOUSLY, right here
```

`.emit()` calls every registered listener for that event **synchronously, in registration order**, before `.emit()` itself returns — this is not a queued or async dispatch (contrast with a browser's DOM events, which can involve capture/bubble phases and different scheduling).

## 2. Extending EventEmitter — the Standard Node Pattern

Almost nobody instantiates `EventEmitter` directly in real code; the standard pattern is to **extend** it so your own class becomes an emitter:

```js
const EventEmitter = require("events");

class Downloader extends EventEmitter {
  start(url) {
    this.emit("start", url);
    // ... simulate work ...
    setTimeout(() => this.emit("progress", 50), 10);
    setTimeout(() => this.emit("done", { url, bytes: 1024 }), 20);
  }
}

const dl = new Downloader();
dl.on("start", (url) => console.log("starting", url));
dl.on("progress", (pct) => console.log("progress:", pct));
dl.on("done", (result) => console.log("done:", result));
dl.start("http://example.com/file.zip");
```

This is exactly how `fs.createReadStream`, `http.Server`, and `child_process.spawn()`'s return values work internally — they're all `EventEmitter` subclasses with domain-specific events layered on top.

## 3. `on` vs `once` vs `off`

```js
const emitter = new EventEmitter();

function handler(msg) { console.log("got:", msg); }

emitter.on("msg", handler);      // persistent — fires every time
emitter.once("msg", (msg) => {   // fires ONCE, then auto-removes itself
  console.log("first time only:", msg);
});

emitter.emit("msg", "a"); // both fire
emitter.emit("msg", "b"); // only `handler` fires now

emitter.off("msg", handler);     // (alias: removeListener) — must pass the SAME function reference
emitter.emit("msg", "c");        // nothing fires
```

Removing a listener requires the exact same function reference that was registered — an anonymous inline arrow function passed to `.on()` can never be `.off()`'d later, because there's no way to refer back to it.

## 4. Multiple Listeners and Removal at Scale

```js
emitter.removeAllListeners("msg");   // remove every listener for one event
emitter.removeAllListeners();        // remove ALL listeners for ALL events — dangerous in shared code

console.log(emitter.listenerCount("msg")); // how many are currently registered
console.log(emitter.eventNames());          // array of every event name with at least one listener
```

## 5. Error Events Are Special

```js
const emitter = new EventEmitter();

emitter.emit("error", new Error("boom")); // throws and CRASHES the process — no 'error' listener registered
```

```js
emitter.on("error", (err) => {
  console.error("handled:", err.message); // now it's just a normal callback, process survives
});
emitter.emit("error", new Error("boom")); // caught by the listener above, no crash
```

The `"error"` event name is hard-coded as special inside `EventEmitter`: emitting it with **zero** listeners attached throws the error synchronously out of `.emit()`, which (if nothing catches it) crashes the process. This is a deliberate design choice — a raised error must not be silently swallowed just because nobody happened to be listening — but it means **every** long-lived `EventEmitter`-based object (especially streams and sockets) needs an `'error'` listener in production code, full stop.

## 6. Async Work Inside Listeners

```js
emitter.on("data", async (chunk) => {
  await process(chunk); // EventEmitter has NO idea this listener is async
});
```

`.emit()` does not await anything — it calls each listener and moves to the next one immediately, regardless of whether a listener returns a Promise. An async listener that rejects produces an **unhandled promise rejection**, not a caught error inside the emitter; `.emit()`'s return value only tells you whether there WAS at least one listener (a boolean), never whether they succeeded.

## 7. `emitter.on(...)` Chaining and Prepending

```js
emitter.on("x", () => {}).on("y", () => {}); // .on() returns `this`, so calls chain

emitter.prependListener("x", fn); // registers `fn` to run FIRST, ahead of already-registered listeners
```

![emit() walks the internal _events map's array for 'data' in registration order, calling each listener synchronously before returning](diagrams/01-emitter-internals.svg)

## Under the Hood: the Internal `_events` Map and the Listener Leak Warning

### `_events` is a plain object mapping event names to listeners

Every `EventEmitter` instance holds an internal `_events` object (not part of the public API, but observable and a real implementation detail worth understanding). Each key is an event name; the value is either:

- a **single function reference**, if exactly one listener is registered for that event (the common case, kept unwrapped for a small memory/speed win), or
- an **array of function references**, once a second listener is added for the same event name.

```js
const emitter = new EventEmitter();
emitter.on("x", () => {});
console.log(emitter._events.x); // a single function

emitter.on("x", () => {});
console.log(Array.isArray(emitter._events.x)); // true — promoted to an array once there are 2+
```

`.emit(name, ...args)` looks up `this._events[name]`; if it's a single function, it calls it directly; if it's an array, it iterates a **copy** of that array (so a listener that adds/removes another listener mid-emit doesn't corrupt the in-progress iteration) and calls each one synchronously, in order, with the same arguments.

### The `maxListeners` leak warning is a heuristic, not a hard limit

```js
const emitter = new EventEmitter();
emitter.setMaxListeners(5); // default is 10

for (let i = 0; i < 11; i++) {
  emitter.on("tick", () => {}); // triggers a MaxListenersExceededWarning after the 11th
}
```

Every time a listener is added, `EventEmitter` checks the current count for that event name against `maxListeners` (default **10**, configurable per-instance via `.setMaxListeners(n)`, or globally via `EventEmitter.defaultMaxListeners`). Exceeding it prints a `MaxListenersExceededWarning` to stderr — this is **not an error and does not stop the listener from being added**; it's purely a leak-detection heuristic, because the single most common cause of "too many listeners on one event" in real code is a bug: something (often inside a loop, or on every request in a server) repeatedly calling `.on()` on the same long-lived emitter without ever removing the old listener, growing unboundedly and leaking memory over the process's lifetime. Legitimately needing more than 10 listeners on one emitter is fine and the limit should just be raised in that case — the warning exists to catch the *accidental*, ever-growing case.

### Why removal requires the exact function reference

Because `_events[name]` stores actual function references (not names, ids, or wrapped descriptors), `.off(name, fn)` has to find `fn` by reference-equality inside that array/slot and splice it out. This is precisely why an anonymous listener can't be removed later — nothing else holds a reference to that same function object to compare against.

## Common Pitfalls

1. **Emitting `"error"` with no listener attached** — crashes the process. Every long-lived emitter (streams, sockets, servers) needs an `'error'` handler in real code.
2. **Passing a fresh arrow function to `.off()`**, expecting it to remove a previously `.on()`'d listener — reference equality fails because it's a different function object, even if the code looks identical.
3. **Registering a new listener inside a hot loop or per-request handler without ever removing it** — the classic listener leak; watch for the `MaxListenersExceededWarning` as a real signal, not noise to suppress.
4. **Assuming `.emit()` waits for async listeners** — it doesn't; it fires-and-forgets each listener call, so ordering guarantees and error handling for async work inside a listener are entirely on you.
5. **Calling `removeAllListeners()` with no arguments on a shared/library-owned emitter** — wipes listeners for every event, including ones other code registered; prefer removing your own specific listener or event name.

## Quick Reference

```
new EventEmitter() / class X extends EventEmitter → the base pattern
.on(name, fn) / .addListener(name, fn) → persistent listener
.once(name, fn)                          → fires once, then auto-removed
.off(name, fn) / .removeListener(name, fn) → needs the SAME function reference
.emit(name, ...args)                       → calls listeners SYNCHRONOUSLY, returns boolean (had listeners?)
.removeAllListeners([name])                  → strips listeners for one event, or ALL events if omitted
.listenerCount(name) / .eventNames()          → introspection
.setMaxListeners(n) / EventEmitter.defaultMaxListeners → leak-detection threshold (default 10)
'error' event                                   → special: throws/crashes if emitted with zero listeners
_events                                          → internal map: function, or array once 2+ listeners
```
