# Events and Utilities

Course: [Node.js](https://www.devtutorials.ai/learn/nodejs) · Section: Asynchronous Node.js
Source: https://www.devtutorials.ai/learn/nodejs/async/events-and-utilities

> The observer pattern and EventEmitter: on, once, off, emit, prependListener, listener helpers, synchronous emit, arguments, the error event, captureRejections, listener limits, extending EventEmitter, events.once and events.on. Error-first callbacks, the util module (promisify, callbackify, inspect, format, types, isDeepStrictEqual, deprecate), cancelling work with AbortController and AbortSignal, and AsyncLocalStorage.

## Events and Utilities

**Event** = a message that says "something happened", such as "the bell rang".

**EventEmitter** = a Node.js object that sends events and calls the functions that listen for them.

- Many Node.js objects are event emitters: servers, streams and `process`.
- One part of a program **emits** an event.
- Other parts **listen** and react.
- The two parts do not need to know each other.

**Simple lesson:** events let one part of a program announce news, and other parts react to it.

### A Real-World Example

Think of the school bell.

- The bell rings once.
- Class 6 goes to lunch. Class 7 goes to the playground. The office closes the gate.
- The bell does not call each class one by one.
- Each class listens for the bell and knows what to do.

The bell is the emitter. Each class is a listener. "Ring" is the event name.

## What You Will Learn

In this lesson, you will learn:

1. The observer pattern and `EventEmitter`
2. `on`, `once`, `off`, `emit` and the listener helpers
3. Why `emit` is synchronous, and how to pass data
4. The special `error` event and listener limits
5. Making your own class from `EventEmitter`
6. `events.once()` and `events.on()` with `await`
7. Error-first callbacks and `util.promisify`
8. The `util` toolbox
9. Cancelling work with `AbortController`, and `AsyncLocalStorage`

## EventEmitter

**Observer pattern** = one object keeps a list of listeners and tells all of them when something happens.

- **`on(name, fn)`** = add a listener.
- **`once(name, fn)`** = add a listener that runs only the first time.
- **`off(name, fn)`** = remove a listener. `removeListener` is the same.
- **`emit(name, ...data)`** = send the event. Returns `true` if anyone was listening.
- **`prependListener(name, fn)`** = add a listener at the front of the line.

Click **▶ Run** and read the terminal.

- **Order:** the prepended listener ran first. The rest ran in the order they were added.
- **once:** the office listener removed itself after one call, so 3 listeners are left.
- **Data:** `"lunch"` was passed to every listener as an argument.

### Listener Helpers

- `listenerCount(name)` = how many listeners an event has.
- `listeners(name)` = the list of listener functions.
- `eventNames()` = all event names that have listeners.
- `removeAllListeners(name)` = remove every listener for that name.

### Synchronous Emit

**`emit` is synchronous.** It calls every listener before the next line runs.

- Events are not a queue. Nothing waits for the event loop.
- A slow listener slows down the code that called `emit`.

`EventEmitter` is a Node.js tool. Browser code uses `addEventListener` on page elements instead.

**Simple lesson:** `on` adds a listener, `emit` calls all listeners at once, in order.

<figure className="ngc-fig node-fig">
<svg viewBox="0 0 560 190" role="img" ariaLabelledby="fig-node-emitter" style={{ width: "100%", maxWidth: 560, height: "auto" }}>
<title id="fig-node-emitter">Animation: emit sends one event to every listener of that name, in the order they were added</title>
<rect x="20" y="70" width="150" height="50" rx="10"  style={{ fill: "var(--ngc-blue-soft)", stroke: "var(--ngc-blue)", strokeWidth: 1.5 }}/><text x="95" y="93" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontFamily: "var(--sans)", fontSize: 13, fontWeight: 700 }}>bell.emit("ring")</text><text x="95" y="110" textAnchor="middle" style={{ fill: "var(--ink)", fontFamily: "var(--sans)", fontSize: 11 }}>the emitter</text>
<rect x="360" y="15" width="180" height="40" rx="10"  style={{ fill: "var(--ngc-green-soft)", stroke: "var(--ngc-green)" }}/><text x="450" y="40" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontFamily: "var(--sans)", fontSize: 13, fontWeight: 700 }}>1. students stand up</text>
<rect x="360" y="75" width="180" height="40" rx="10"  style={{ fill: "var(--ngc-green-soft)", stroke: "var(--ngc-green)" }}/><text x="450" y="100" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontFamily: "var(--sans)", fontSize: 13, fontWeight: 700 }}>2. teacher starts class</text>
<rect x="360" y="135" width="180" height="40" rx="10"  style={{ fill: "var(--ngc-green-soft)", stroke: "var(--ngc-green)" }}/><text x="450" y="160" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontFamily: "var(--sans)", fontSize: 13, fontWeight: 700 }}>3. log "ring"</text>
<line x1="170" y1="95" x2="360" y2="35"  style={{ stroke: "var(--ink)", strokeDasharray: "4 4" }}/><line x1="170" y1="95" x2="360" y2="95"  style={{ stroke: "var(--ink)", strokeDasharray: "4 4" }}/><line x1="170" y1="95" x2="360" y2="155"  style={{ stroke: "var(--ink)", strokeDasharray: "4 4" }}/>
<circle r="7" cx="170" cy="95" style={{ fill: "var(--ngc-orange)" }}><animate attributeName="cx" values="170;360;360" keyTimes="0;0.25;1" dur="6s" repeatCount="indefinite" /><animate attributeName="cy" values="95;35;35" keyTimes="0;0.25;1" dur="6s" repeatCount="indefinite" /></circle>
<circle r="7" cx="170" cy="95" opacity="0" style={{ fill: "var(--ngc-orange)" }}><animate attributeName="opacity" values="0;0;1;1" keyTimes="0;0.3;0.31;1" dur="6s" repeatCount="indefinite" /><animate attributeName="cx" values="170;170;360;360" keyTimes="0;0.3;0.55;1" dur="6s" repeatCount="indefinite" /></circle>
<circle r="7" cx="170" cy="95" opacity="0" style={{ fill: "var(--ngc-orange)" }}><animate attributeName="opacity" values="0;0;1;1" keyTimes="0;0.6;0.61;1" dur="6s" repeatCount="indefinite" /><animate attributeName="cx" values="170;170;360;360" keyTimes="0;0.6;0.85;1" dur="6s" repeatCount="indefinite" /><animate attributeName="cy" values="95;95;155;155" keyTimes="0;0.6;0.85;1" dur="6s" repeatCount="indefinite" /></circle>
</svg>
<figcaption>One emit, many listeners. They run one after another, in the order you added them.</figcaption>
</figure>

## The Special error Event

- An event named `error` is special.
- If you emit `error` and **nobody listens**, Node.js throws the error and the program crashes.
- Always add an `error` listener to emitters that can fail, such as streams and servers.

### captureRejections

- An `async` listener returns a promise. If it fails, `emit` does not notice.
- `new EventEmitter({ captureRejections: true })` sends such failures to the `error` event.

### Listener Limits and Memory Leaks

- By default, one event may have up to 10 listeners.
- An 11th listener prints a `MaxListenersExceededWarning`.
- This usually means code adds a listener again and again, for example inside a loop. That is a **memory leak**.
- If you really need more, call `emitter.setMaxListeners(20)`.

To remove a listener, pass the same function you added. Keep it in a named variable.

**Simple lesson:** always listen for `error`, and remove listeners you no longer need.

## Extending EventEmitter

Your own classes can send events by extending `EventEmitter`.

<figure className="ngc-fig node-fig">
<svg viewBox="0 0 560 200" role="img" ariaLabelledby="fig-node-emitter" style={{ width: "100%", maxWidth: 560, height: "auto" }}>
<title id="fig-node-emitter">Animation: a Classroom object emits a joined event and three listeners react one after another</title>
<rect x="20" y="70" width="170" height="60" rx="10"  style={{ fill: "var(--ngc-purple-soft)", stroke: "var(--ngc-purple)", strokeWidth: 1.5 }}/>
<text x="105" y="96" textAnchor="middle" style={{ fill: "var(--ngc-purple)", fontFamily: "var(--sans)", fontSize: 13, fontWeight: 700 }}>Classroom</text>
<text x="105" y="116" textAnchor="middle" style={{ fill: "var(--ink)", fontFamily: "var(--mono)", fontSize: 11 }}>emit("joined", "Asha")</text>
<rect x="360" y="15" width="180" height="40" rx="8"  style={{ fill: "var(--ngc-blue-soft)", stroke: "var(--ngc-blue)" }}/><text x="450" y="40" textAnchor="middle" style={{ fill: "var(--ngc-blue)", fontFamily: "var(--sans)", fontSize: 12 }}>Register: mark present</text>
<rect x="360" y="80" width="180" height="40" rx="8"  style={{ fill: "var(--ngc-green-soft)", stroke: "var(--ngc-green)" }}/><text x="450" y="105" textAnchor="middle" style={{ fill: "var(--ngc-green)", fontFamily: "var(--sans)", fontSize: 12 }}>Teacher: say hello</text>
<rect x="360" y="145" width="180" height="40" rx="8"  style={{ fill: "var(--ngc-orange-soft)", stroke: "var(--ngc-orange)" }}/><text x="450" y="170" textAnchor="middle" style={{ fill: "var(--ngc-orange)", fontFamily: "var(--sans)", fontSize: 12 }}>Counter: count + 1</text>
<line x1="190" y1="100" x2="360" y2="35"  style={{ stroke: "var(--ngc-slate)", strokeDasharray: "4 4" }}/>
<line x1="190" y1="100" x2="360" y2="100"  style={{ stroke: "var(--ngc-slate)", strokeDasharray: "4 4" }}/>
<line x1="190" y1="100" x2="360" y2="165"  style={{ stroke: "var(--ngc-slate)", strokeDasharray: "4 4" }}/>
<circle r="6" cx="190" cy="100" style={{ fill: "var(--ngc-purple)" }}>
<animate attributeName="cx" values="190;360;190;360;190;360" keyTimes="0;0.2;0.33;0.53;0.66;0.86" dur="6s" repeatCount="indefinite" />
<animate attributeName="cy" values="100;35;100;100;100;165" keyTimes="0;0.2;0.33;0.53;0.66;0.86" dur="6s" repeatCount="indefinite" />
</circle>
</svg>
<figcaption>One emit, three listeners, called one after another.</figcaption>
</figure>

### events.once() and events.on()

- **`events.once(emitter, name)`** = a promise that waits for one event. Use it with `await`.
- **`events.on(emitter, name)`** = an async iterator. Use it with `for await` to handle many events.

**Simple lesson:** extend `EventEmitter` to give your own objects events, and `await once()` to wait for one.

## Callback Conventions

**Error-first callback** = a callback whose first argument is an error, or `null` when all went well.

```js
fs.readFile("marks.txt", "utf8", (err, data) => {
  if (err) {
    // handle the problem
    return;
  }
  // use data
});
```

- Older Node.js APIs use callbacks.
- Newer APIs return promises, such as `node:fs/promises`.
- Most callback APIs have a promise twin. Prefer the promise version in new code.

`fs/promises`, `timers/promises`, `stream/promises`, `readline/promises` and `dns/promises` are the promise versions of their modules.

**Simple lesson:** in a callback API, always check `err` first.

## The util Module

`node:util` is a toolbox of small helpers.

- **`promisify(fn)`** = turn an error-first callback function into a function that returns a promise.
- **`callbackify(fn)`** = the opposite: turn an async function into a callback function.
- **`inspect(value)`** = the text `console.log` would print for a value.
- **`format("%s has %d", a, b)`** = fill in a text template.
- **`types`** = checks such as `util.types.isPromise(x)`.
- **`isDeepStrictEqual(a, b)`** = are two objects equal, inside and out?
- **`deprecate(fn, message)`** = wrap an old function so it prints a warning once.

**Simple lesson:** reach for `util` before writing your own helper, especially `promisify`.

## Cancelling Operations

**AbortController** = a remote control that can cancel work.

- `const ac = new AbortController()` makes the controller.
- `ac.signal` = the **signal** you pass to the work.
- `ac.abort()` = press cancel. The work stops with an `AbortError`.
- **`AbortSignal.timeout(ms)`** = a signal that cancels by itself after `ms` milliseconds.
- Many Node.js APIs take `{ signal }`: `timers/promises`, `events.once`, `fs/promises`, `child_process`, `http` and `fetch`.

**Simple lesson:** pass a `signal` to slow work, so you can cancel it.

## AsyncLocalStorage (Advanced)

**AsyncLocalStorage** = a store that follows one chain of async work, such as one web request.

- A server handles many requests at the same time.
- You want every log line to show which request it belongs to.
- Passing a request id through every function is tiring.
- `als.run(store, fn)` starts a chain. `als.getStore()` reads the store anywhere inside it, even after `await`.
- Common uses: request ids in logs, the current user, timing.

```js
// main.mjs
import { AsyncLocalStorage } from "node:async_hooks";
import { setTimeout as wait } from "node:timers/promises";

const als = new AsyncLocalStorage();

function log(msg) {
  console.log(`[request ${als.getStore().id}] ${msg}`);
}

async function handle(id, delay) {
  await als.run({ id }, async () => {
    log("started");
    await wait(delay);
    log("finished");
  });
}

await Promise.all([handle(1, 30), handle(2, 10)]);
```

Output:

```text
[request 1] started
[request 2] started
[request 2] finished
[request 1] finished
```

- Request 2 finished first, but each line still shows the right id.

**Simple lesson:** AsyncLocalStorage remembers "which request am I in" across `await`.

## Common mistakes

<div className="tscroll">

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| No `error` listener | An unhandled error event is ignored | Always add `on("error", ...)` | The program crashes |
| Adding a listener inside a loop or a request | Listeners clean themselves up | Add once, or remove with `off` | Memory leak and `MaxListenersExceededWarning` |
| Removing a listener with a new arrow function | `off` matches by code | Keep a name for the function and pass the same one | The listener is never removed |
| Expecting `emit` to wait for the event loop | Events are queued | `emit` calls listeners at once | Code runs in a surprising order |
| Ignoring `err` in a callback | `err` is only for logging | Check `err` first and return | Code uses `undefined` data |
| Using `promisify` on a function without an error-first callback | It works on any callback | Only for `(err, value)` callbacks | The promise never settles, or the order is wrong |

</div>

## TL;DR

1. **EventEmitter** = sends named events to listeners.
   - `on` add, `once` one time, `off` remove, `emit` send
2. **`emit` is synchronous** = listeners run before the next line.
3. **`error` event** = crashes the program if nobody listens.
4. **Listener limit** = 10 by default. More usually means a leak.
5. **`events.once`** = await one event.
6. **Error-first callback** = `(err, value)`. Check `err` first.
7. **`util.promisify`** = callback function to promise function.
8. **AbortController** = cancel work through a `signal`.

## Check Yourself

## Exercises

## What to Remember

- An `EventEmitter` calls its listeners at once, in order, when you `emit`.
- `once` listens one time. `off` needs the same function you added.
- Always handle the `error` event.
- Extend `EventEmitter` to give your own classes events.
- `util.promisify` turns old callbacks into promises.
- Pass an `AbortSignal` to slow work so it can be cancelled.

Next: **Error Handling**. You will read error messages, handle failures and decide when a program should stop.
