什么时候我应该使用EventEmitter

When should I use EventEmitter?

本文关键字:EventEmitter 我应该 什么时候      更新时间:2023-09-26

我读了很多关于EventEmitter的事情。但我不知道在哪种情况下我需要在我的Node.js应用程序中使用它

对于代码来说订阅某个内容而不是从某个内容获取回调是有意义的。典型的用例是,当一个事件发生时,应用程序中有多个代码块可能需要做一些事情。

例如,假设您正在创建一个票务系统。处理这些事情的常见方法可能是这样的:

function addTicket(ticket, callback) {
    insertTicketIntoDatabase(ticket, function(err) {
        if (err)
            return handleError(err);
        callback();
    });
}

但是现在,有人决定当一张票被插入到数据库中时,你应该给用户发电子邮件让他们知道。很好,你可以把它添加到回调中:

function addTicket(ticket, callback) {
    insertTicketIntoDatabase(ticket, function(err) {
        if (err)
            return handleError(err);
        emailUser(ticket, callback);
    });
}

但是现在,有人想通知另一个系统票已经被插入。随着时间的推移,当一张罚单被插入时,可能会发生很多事情。让我们稍微改变一下:

function addTicket(ticket, callback) {
    insertTicketIntoDatabase(ticket, function(err) {
        if (err)
            return handleError(err);
        TicketEvent.emit('inserted', ticket);
        callback();
    });
}

在通知用户界面之前,我们不再需要等待所有这些函数完成。在代码的其他地方,您可以轻松地添加这些函数:

TicketEvent.on('inserted', function(ticket) {
    emailUser(ticket);
});
TicketEvent.on('inserted', function(ticket) {
    notifySlack(ticket);
});
  • 当同一个事件可以发生多个时,应该使用EventEmitter有时,也可能根本不会发生。实际上,期望调用回调就一次,不管手术成功与否。回叫的意思是等你准备好了再打电话给我

  • 使用回调的API只能通知一个特定的回调使用EventEmitter允许我们为事件注册多个监听器相同的事件。

  • 如果你需要通知用户状态变化,使用事件发射器。

  • 为了测试的目的,如果你想确保一个函数在函数内部被调用,触发一个事件。

Node.js事件发射器在希望将代码库分解为使用类似于发布-订阅的异步模式调用的组件或服务。然而,通常当我们谈论发布-订阅模式时,我们指的是分布式分解系统。这里的情况并非如此,因为所有组件都存在于相同的代码库中,并在相同的Node.js运行时中运行。

请记住,使用Node.js事件发射器不会使我们的代码自动非阻塞,异步。需要特别注意的是,事件侦听器(订阅者)不会相互阻塞,也就是说,事件侦听器应该异步执行代码。

此外,在使用此模式时,事件发射器(发布者)不关心事件侦听器所采取的操作的结果。没有回调或返回值。如果这些操作是关键的,则需要处理故障。

代码示例:

/**
 * 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');