'When should I use EventEmitter?

I read a lot of things about EventEmitter. But I do not know in which case I need to use it in my Node.js application.



Solution 1:[1]

  • The EventEmitter should be used when the same event can occur multiple times, or may not occur at all. A callback, in fact, is expected to be invoked exactly once, whether the operation is successful or not. Callback means call me when you are ready

  • An API that uses callbacks can notify only one particular callback while using an EventEmitter allows us to register multiple listeners for the same event.

  • Use event emitter if you need to notify the user of a state change.

  • For testing purpose, if you want to make sure a function is called inside a function, emit an event.

Solution 2:[2]

Node.js Event Emitter is used when there is a desire to decompose a codebase into components or services invoked using a pub-sub like asynchronous pattern. However, typically when we talk about pub-sub pattern we refer to distributed decomposed systems. This is not the case here as all components exist in the same code repository and run in the same Node.js runtime.

Keep in mind that using Node.js Event Emitter does not make our code automatically non-blocking, asynchronous. A special care needs to be taken so that event listeners (subscribers) do not block each other, that is the event listeners should execute code asynchronously.

In addition, when using this pattern event emitters (publishers) do not care about the result of the actions taken by the event listeners. There is no callback or return value. If these actions are critical then failures need to be handled.

Code examples:

/**
 * When event listeners execute synchronous blocking code as seen in this example,
 * the next listener is not notified until the first listener completes execution
 * of the synchronous blocking code.
 *
 * Here is an output from running this code:
 *
 * 11:16:40 Listener 1 - processing event
 * 11:16:45 Listener 1 - processed: Test Event
 * 11:16:45 Listener 2 - processing event
 * 11:16:45 Listener 2 - processed: Test Event
 */

const { EventEmitter } = require('events');

const time = () => {
  const currentDate = new Date();
  return `${currentDate.getHours()}:${currentDate.getMinutes()}:${currentDate.getSeconds()}`;
};

const EventBus = new EventEmitter();

// Listener 1
EventBus.on('event', (message) => {
  console.log(`${time()} Listener 1 - processing event`);
  for (let i = 0; i < 6e9; i += 1) {
    // Intentionally empty
  }
  console.log(`${time()} Listener 1 - processed: ${message}`);
});

// Listener 2
EventBus.on('event', (message) => {
  console.log(`${time()} Listener 2 - processing event`);
  console.log(`${time()} Listener 2 - processed: ${message}`);
});

// Emitting event
EventBus.emit('event', 'Test Event');

/**
 *
 * To take full advantage of EventListener the listeners should execute
 * asynchronous non-blocking code. However, wrapping a synchronous code
 * into an async function is not enough. The 2nd listener is still
 * blocked and waiting for the async function to complete
 *
 * Here is an output from running this code:
 * 11:13:52 Listener 1 - processing event
 * 11:13:52 Listener 1 - about to await
 * 11:13:57 Listener 2 - processing event
 * 11:13:57 Listener 2 - processed: Test Event
 * 11:13:57 Listener 1 - await completed
 * 11:13:57 Listener 1 - processed: Test Event
 */

const { EventEmitter } = require('events');

const time = () => {
  const currentDate = new Date();
  return `${currentDate.getHours()}:${currentDate.getMinutes()}:${currentDate.getSeconds()}`;
};

const EventBus = new EventEmitter();

// Listener 1
EventBus.on('event', async (message) => {
  console.log(`${time()} Listener 1 - processing event`);

  async function extracted() {
    for (let i = 0; i < 6e9; i += 1) {
      // Intentionally empty
    }
  }

  console.log(`${time()} Listener 1 - about to await`);
  await extracted();
  console.log(`${time()} Listener 1 - await completed`);
  console.log(`${time()} Listener 1 - processed: ${message}`);
});

// Listener 2
EventBus.on('event', (message) => {
  console.log(`${time()} Listener 2 - processing event`);
  console.log(`${time()} Listener 2 - processed: ${message}`);
});

// Emitting event
EventBus.emit('event', 'Test Event');
/**
 *
 * To take full advantage of EventListener the listeners should execute
 * asynchronous non-blocking code. Here we are using setTimeout() in order
 * to execute code asynchronously.
 *
 * Here is an output from running this code:
 *
 * 11:45:54 Listener 1 - processing event
 * 11:45:54 Listener 1 - about to execute setTimeout
 * 11:45:54 Listener 1 - setTimeout completed
 * 11:45:54 Listener 1 - processed: Test Event
 * 11:45:54 Listener 2 - processing event
 * 11:45:54 Listener 2 - processed: Test Event
 * 11:45:59 Listener 1 - finished the long loop
 */

const { EventEmitter } = require('events');

const time = () => {
  const currentDate = new Date();
  return `${currentDate.getHours()}:${currentDate.getMinutes()}:${currentDate.getSeconds()}`;
};

const EventBus = new EventEmitter();

// Listener 1
EventBus.on('event', async (message) => {
  console.log(`${time()} Listener 1 - processing event`);

  function extracted() {
    for (let i = 0; i < 6e9; i += 1) {
      // Intentionally empty
    }
    console.log(`${time()} Listener 1 - finished the long loop`);
  }

  console.log(`${time()} Listener 1 - about to execute setTimeout`);
  setTimeout(extracted, 0);
  console.log(`${time()} Listener 1 - setTimeout completed`);
  console.log(`${time()} Listener 1 - processed: ${message}`);
});

// Listener 2
EventBus.on('event', (message) => {
  console.log(`${time()} Listener 2 - processing event`);
  console.log(`${time()} Listener 2 - processed: ${message}`);
});

// Emitting event
EventBus.emit('event', 'Test Event');
/**
 *
 * To take full advantage of EventListener the listeners should execute
 * asynchronous non-blocking code. Here we are using setImmediate() in order
 * to execute code asynchronously.
 *
 * Here is an output from running this code:
 *
 * 12:1:3 Listener 1 - processing event
 * 12:1:3 Listener 1 - about to execute setImmediate
 * 12:1:3 Listener 1 - setImmediate completed
 * 12:1:3 Listener 1 - processed: Test Event
 * 12:1:3 Listener 2 - processing event
 * 12:1:3 Listener 2 - processed: Test Event
 * 12:1:9 Listener 1 - finished the long loop
 */

const { EventEmitter } = require('events');

const time = () => {
  const currentDate = new Date();
  return `${currentDate.getHours()}:${currentDate.getMinutes()}:${currentDate.getSeconds()}`;
};

const EventBus = new EventEmitter();

// Listener 1
EventBus.on('event', async (message) => {
  console.log(`${time()} Listener 1 - processing event`);

  function extracted() {
    for (let i = 0; i < 6e9; i += 1) {
      // Intentionally empty
    }
    console.log(`${time()} Listener 1 - finished the long loop`);
  }

  console.log(`${time()} Listener 1 - about to execute setImmediate`);
  setImmediate(extracted);
  console.log(`${time()} Listener 1 - setImmediate completed`);
  console.log(`${time()} Listener 1 - processed: ${message}`);
});

// Listener 2
EventBus.on('event', (message) => {
  console.log(`${time()} Listener 2 - processing event`);
  console.log(`${time()} Listener 2 - processed: ${message}`);
});

// Emitting event
EventBus.emit('event', 'Test Event');

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 OSGI Java