Suggest an editImprove this articleRefine the answer for “What is eventemitter in Node.js?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**EventEmitter** is a Node.js class from the `events` module that lets objects emit named events and attach listener functions to handle them. ```js const EventEmitter = require('events'); const emitter = new EventEmitter(); emitter.on('message', (msg) => console.log(`Received: ${msg}`)); emitter.emit('message', 'hello'); // → Received: hello ``` **Key point:** `emit` is synchronous - all listeners run before the next line executes.Shown above the full answer for quick recall.Answer (EN)Image**EventEmitter** is a Node.js class from the `events` module that lets objects emit named events and register listener functions to handle them. ## Theory ### TL;DR - Think of EventEmitter like a dinner bell in a restaurant kitchen: ring it with a name ("order ready"), and only the listeners subscribed to that name react. - `on()` adds a persistent listener; `once()` adds one that removes itself after the first fire. - `emit()` is synchronous - it calls all listeners in registration order before returning. - Always listen for the `'error'` event, or Node.js will throw and crash the process. - Use EventEmitter for in-process pub-sub; switch to Redis or Kafka for distributed systems. ### Quick example ```js const EventEmitter = require('events'); const emitter = new EventEmitter(); // Persistent listener - fires every time emitter.on('order', (dish) => console.log(`Serving ${dish}`)); // One-time listener - auto-removed after first call emitter.once('alert', () => console.log('Evacuation!')); emitter.emit('order', 'pizza'); // → Serving pizza emitter.emit('alert'); // → Evacuation! emitter.emit('alert'); // → (nothing - already fired) ``` `on()` keeps the listener alive across multiple emits. `once()` removes itself automatically after the first call. ### How it works internally EventEmitter stores listeners in an internal `_events` object - a map where each key is an event name and the value is an array of functions. When you call `emit('order', 'pizza')`, Node grabs that array and calls each function synchronously, in registration order, passing `'pizza'` as the argument. No async queueing happens here. The call stack does not return until every listener finishes. So if a listener does something slow - like a blocking loop - it holds up everything that follows. ### When to use - User login needs to trigger both an email and an analytics event from one place: EventEmitter. - You need a signal that fires only once (connection established, file opened): `once()`. - Working with Node.js streams: they already extend EventEmitter, so `'data'`, `'end'`, and `'error'` come built in. - Events need to cross process or server boundaries: skip EventEmitter and use a message queue like BullMQ or Redis Pub/Sub. ### Custom class pattern The most common production pattern is extending EventEmitter directly: ```js const EventEmitter = require('events'); class Database extends EventEmitter { connect() { setTimeout(() => { this.emit('connected', { host: 'localhost' }); }, 1000); } query(sql) { setTimeout(() => { this.emit('data', [{ id: 1, name: 'Alice' }]); }, 500); } } const db = new Database(); db.on('connected', ({ host }) => { console.log(`Connected to ${host}`); db.query('SELECT * FROM users'); }); db.on('data', (rows) => { console.log('Rows:', rows); }); db.connect(); ``` The `Database` class stays focused on data logic. Callers decide what to do with each event. That separation is the point. ### Common mistakes **1. Missing the `'error'` listener** ```js const ee = new EventEmitter(); ee.emit('error', new Error('boom')); // Uncaught exception - process exits ``` Node.js treats `'error'` as special. Emit it with no listener and the process crashes. Always add one: ```js ee.on('error', (err) => console.error('Handled:', err.message)); ``` **2. Assuming `emit` is async** ```js ee.on('heavy', () => { for (let i = 0; i < 1e8; i++) {} // blocking loop }); ee.emit('heavy'); ee.emit('light'); // delayed until 'heavy' listener finishes ``` `emit` is synchronous. If you need to offload slow work, use `process.nextTick()` or a worker thread inside the listener. **3. Removing the wrong listener** ```js ee.on('greet', () => console.log('hi')); ee.off('greet', () => console.log('hi')); // Listener still active! Different reference. ``` Arrow functions create a new reference every time. Store the function first: ```js const handler = () => console.log('hi'); ee.on('greet', handler); ee.off('greet', handler); // Works correctly ``` **4. Hitting the max listeners warning** Node.js warns when more than 10 listeners are added to a single event - this catches accidental leaks in loops. If you genuinely need more, set the limit explicitly: ```js emitter.setMaxListeners(20); emitter.getMaxListeners(); // → 20 ``` ### Real-world usage - Node.js streams (`fs.ReadStream`, `net.Socket`) extend EventEmitter and emit `'data'`, `'end'`, `'error'`. - `http.Server` emits `'request'` for each incoming HTTP connection. - Socket.io uses EventEmitter as the base for its client-server event model. - Webpack's compiler emits `'done'` and `'invalid'` to power hot module replacement. - The `process` object itself is an EventEmitter: `'exit'`, `'uncaughtException'`, `'SIGTERM'`. In practice, the `'error'` listener is the thing most teams skip in early prototypes, and it always comes back to bite them in staging. ### Follow-up questions **Q:** What is the difference between `on` and `once`? **A:** `on` adds a persistent listener that fires every time the event is emitted. `once` wraps the listener, removes it after the first call, then invokes the original. Use `once` for acknowledgments or one-shot setup steps. **Q:** Is `emit` synchronous or asynchronous? **A:** Synchronous. It calls all listeners before returning. If you call `emit` inside a `setTimeout`, the emit itself is still sync within that callback. **Q:** What happens if a listener throws an error? **A:** The error propagates up the call stack. If an `'error'` listener exists and the throwing event is `'error'`, it catches there. Otherwise the exception is unhandled and can crash the process. **Q:** How do you detect memory leaks with EventEmitter? **A:** Use `emitter.eventNames()` to list registered events and `emitter.listenerCount('eventName')` to count listeners. Node logs a warning automatically when a single event has more than 10 listeners. **Q:** Implement a minimal EventEmitter with `off` support. **A:** Use a `Map<string, Set<Function>>`. `on` adds to the set, `off` deletes from it (O(1) removal by reference), and `emit` iterates `Array.from(set)` to avoid mutation issues during the loop. ## Examples ### Pub-sub with multiple listeners ```js const EventEmitter = require('events'); const bus = new EventEmitter(); // Two independent listeners on the same event bus.on('login', (user) => console.log(`Send welcome email to ${user.email}`)); bus.on('login', (user) => console.log(`Track login for user ${user.id}`)); bus.emit('login', { id: 42, email: 'alice@example.com' }); // → Send welcome email to alice@example.com // → Track login for user 42 ``` Both listeners fire in registration order. Neither knows the other exists. This is the decoupling that makes EventEmitter worth reaching for. ### Express request logger using EventEmitter ```js const EventEmitter = require('events'); const express = require('express'); const app = express(); const logger = new EventEmitter(); logger.on('request', ({ method, url }) => { console.log(`${method} ${url} at ${new Date().toISOString()}`); }); app.use((req, res, next) => { logger.emit('request', { method: req.method, url: req.url }); next(); }); app.get('/users', (req, res) => res.send('Users list')); app.listen(3000); // GET /users → GET /users at 2024-01-15T10:30:00.000Z ``` The route handler has no idea logging exists. Swap out the logger listener any time without touching route code. ### Extending EventEmitter for a file watcher ```js const EventEmitter = require('events'); const fs = require('fs'); class FileWatcher extends EventEmitter { watch(filePath) { fs.watchFile(filePath, { interval: 500 }, (curr, prev) => { if (curr.mtime > prev.mtime) { this.emit('change', { path: filePath, modified: curr.mtime }); } }); } } const watcher = new FileWatcher(); watcher.on('error', (err) => console.error('Watcher error:', err)); watcher.on('change', ({ path, modified }) => { console.log(`${path} changed at ${modified}`); }); watcher.watch('./config.json'); ``` The class emits `'change'` when the file updates. Callers decide what to do with that signal - hot reload, re-parse the config, notify a dashboard.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.