Events Module in Node.js: Event-Driven Architecture

Much of the Node.js core API is built around an asynchronous, event-driven architecture. In this model, certain objects called emitters generate named events, and functions called listeners respond to those events. This design makes it easier to handle I/O operations, streams, and asynchronous workflows in a clean and efficient way.

EventEmitteremit and ononce and prependListenererror eventscaptureRejections

~5 دقیقه مطالعه · آخرین به‌روزرسانی ۶ دی ۱۴۰۴

1. What is EventEmitter?


All objects that emit events are instances of the EventEmitter class. This class provides methods like on() to register listeners and emit() to trigger events.


2. Simple Example


const EventEmitter = require('node:events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();

myEmitter.on('event', () => {
  console.log('an event occurred!');
});
myEmitter.emit('event');

3. Passing Arguments and this


emit() can pass arguments to listeners. In regular functions, this refers to the EventEmitter instance. With arrow functions, this does not reference the EventEmitter.


4. Synchronous vs. Asynchronous


Listeners are called synchronously in the order they were registered. To switch to asynchronous execution, use setImmediate() or process.nextTick().


5. One-Time Execution


once() registers a listener that runs only once and is then removed.


6. Error Events


If an EventEmitter emits an 'error' event without a registered listener, Node.js will throw the error and crash. Always register error listeners to prevent this.


myEmitter.on('error', (err) => {
  console.error('Error occurred:', err);
});

7. captureRejections


Async listeners can cause unhandled Promise rejections. The captureRejections option routes these rejections to the 'error' event or Symbol.for('nodejs.rejection').


8. Special EventEmitter Events


  • 'newListener': Emitted before a new listener is added.
  • 'removeListener': Emitted after a listener is removed.

9. Key EventEmitter Methods


  • on(): Register a listener.
  • once(): Register a one-time listener.
  • emit(): Trigger an event.
  • listeners(): Get a list of listeners.
  • eventNames(): Get registered event names.
  • setMaxListeners(): Set the maximum number of listeners.
  • prependListener(): Add a listener at the beginning of the list.

Conclusion


The Events module is central to Node.js’s event-driven architecture. By using EventEmitter and its methods, developers can build robust asynchronous applications that handle streams, connections, and complex workflows efficiently.


1. prependListener()


Adds a listener to the beginning of the listener array for a given event.


server.prependListener('connection', (stream) => {
  console.log('someone connected!');
});

2. prependOnceListener()


Similar to prependListener(), but the listener is invoked only once and then removed.


server.prependOnceListener('connection', (stream) => {
  console.log('Ah, we have our first user!');
});

3. removeListener() and removeAllListeners()


  • removeListener(eventName, listener): Removes a specific listener.
  • removeAllListeners([eventName]): Removes all listeners or all listeners for a specific event.

Note: Removing listeners added by other modules can cause issues and is considered bad practice.


4. setMaxListeners() and defaultMaxListeners


By default, Node.js warns if more than 10 listeners are added to a single event. setMaxListeners(n) changes this limit for a specific instance, while events.defaultMaxListeners changes the default globally.


5. rawListeners()


Returns an array of raw listeners, including wrappers created by methods like once().


const listeners = emitter.rawListeners('log');
listeners[0].listener(); // Executes the original listener without removing it
listeners[0]();          // Executes and removes the listener

6. errorMonitor


events.errorMonitor allows monitoring of error events without consuming them. These listeners run before regular error listeners.


7. events.once()


Creates a Promise that resolves when the specified event is emitted, or rejects if an error occurs.


const { once, EventEmitter } = require('node:events');
const ee = new EventEmitter();

(async () => {
  const [value] = await once(ee, 'myevent');
  console.log(value);
})();

8. Managing Rejections


With captureRejections enabled, rejected Promises in async listeners are automatically routed to the 'error' event or Symbol.for('nodejs.rejection').


Conclusion


Advanced EventEmitter methods in Node.js provide powerful tools for listener management, error handling, and preventing memory leaks. Using these methods correctly enhances the stability and maintainability of event-driven applications.


1. events.listenerCount()


Returns the number of listeners registered for a given event. Deprecated since v3.2.0; use emitter.listenerCount() instead.


const { EventEmitter, listenerCount } = require('node:events');
const myEmitter = new EventEmitter();
myEmitter.on('event', () => {});
myEmitter.on('event', () => {});
console.log(listenerCount(myEmitter, 'event')); // 2

2. events.on()


Creates an AsyncIterator that iterates over emitted events. Each iteration returns an array of arguments passed to the event.


const { on, EventEmitter } = require('node:events');
(async () => {
  const ee = new EventEmitter();
  process.nextTick(() => {
    ee.emit('foo', 'bar');
    ee.emit('foo', 42);
  });
  for await (const event of on(ee, 'foo')) {
    console.log(event); // ['bar'], [42]
  }
})();

3. events.setMaxListeners()


Sets the maximum number of listeners for one or more EventEmitters or EventTargets. Default is 10, but can be changed globally or per instance.


const { setMaxListeners, EventEmitter } = require('node:events');
const emitter = new EventEmitter();
setMaxListeners(5, emitter);

4. events.addAbortListener()


Safely listens for the abort event on an AbortSignal. Returns a disposable object to easily unsubscribe.


const { addAbortListener } = require('node:events');
function example(signal) {
  const disposable = addAbortListener(signal, () => {
    console.log('Aborted!');
  });
  disposable[Symbol.dispose]();
}

5. EventEmitterAsyncResource


Integrates EventEmitter with AsyncResource for manual async tracking. Events emitted by this class run within its async context.


const { EventEmitterAsyncResource } = require('node:events');
const ee = new EventEmitterAsyncResource({ name: 'Q' });
ee.on('foo', () => {
  console.log('running in async context');
});
ee.emit('foo');

6. EventTarget and NodeEventTarget


Node.js implements its own EventTarget API, similar to the web standard but with differences:


  • No hierarchy or event propagation.
  • Listeners can only be registered once per event type.
  • NodeEventTarget emulates a subset of EventEmitter methods but is not a full replacement.

const target = new EventTarget();
target.addEventListener('foo', (event) => {
  console.log('foo event happened!');
});
target.dispatchEvent(new Event('foo'));

Conclusion


The advanced features of the events module in Node.js provide powerful tools for listener management, async event handling, and integration with AbortSignals and async contexts. Understanding these APIs helps developers build robust, event-driven applications that align with both Node.js and web standards.


نوشته و پژوهش‌شده توسط دکتر شاهین صیامی

مقالات مرتبط

Comprehensive Guide to the Node.js VM Module (node:vm)

The node:vm module allows you to compile and execute JavaScript code inside isolated V8 contexts — essentially creating lightweight sandboxes within your Node.js application. These contexts have their own global scope and can run code independently from the main environment. However, vm is NOT a security sandbox. It is powerful for dynamic code execution, template engines, REPLs, plugin systems, and controlled module execution, but it must never be used to run untrusted code.

ادامه

Comprehensive Guide to the V8 Module in Node.js (node:v8)

The node:v8 module exposes low-level APIs that interact directly with the V8 JavaScript engine embedded in Node.js. . These APIs provide access to heap statistics, heap snapshots, coverage tools, serialization mechanisms, V8 flags, object queries, and promise lifecycle hooks. The module is essential for performance analysis, memory debugging, tooling, and advanced Node.js internals work.

ادامه

Comprehensive Guide to Node.js Worker Threads (node:worker_threads)

The node:worker_threads module enables true multithreading in Node.js by running JavaScript in separate threads. While Node.js is traditionally single‑threaded, worker threads allow CPU‑intensive tasks to run in parallel without blocking the event loop. They support shared memory, zero‑copy transfers, worker pools, resource limits, and advanced synchronization APIs. Worker threads are ideal for heavy computation, data processing, and parallel workloads—while async I/O remains best handled by the main thread.

ادامه

Comprehensive Guide to the Node.js util Module (node:util)

The node:util module provides a powerful collection of helper functions used throughout Node.js core and extremely useful for application developers. These utilities support debugging, inspection, formatting, type checking, callback/Promise conversions, argument parsing, text encoding, MIME handling, and more. It is one of the most versatile and essential toolkits in the Node.js ecosystem.

ادامه

Comprehensive Guide to the URL Module in Node.js

The node:url module provides tools for parsing, constructing, and manipulating URLs. Node.js supports two URL APIs: WHATWG URL API — modern, browser‑compatible, standards‑based. Legacy Node.js URL API — older, Node‑specific, now discouraged. The WHATWG API is the recommended approach for all modern applications. It provides a clean, consistent interface for working with URL components, query parameters, and structured URL patterns.

ادامه

راهنمای جامع UDP / Datagram Sockets در Node.js

ماژول node:dgram پیاده‌سازی کامل سوکت‌های UDP را در Node.js فراهم می‌کند. UDP یک پروتکل سبک، بدون اتصال (connectionless) و مناسب برای برنامه‌های بلادرنگ مانند VoIP، بازی‌ها، IoT، سیستم‌های پخش (broadcast) و چندپخشی (multicast) است. این ماژول امکان ساخت سوکت، ارسال و دریافت دیتاگرام، مدیریت TTL، عضویت در گروه‌های multicast، کنترل بافرها، و مدیریت رفتار سطح پایین شبکه را فراهم می‌کند.

ادامه