简体   繁体   English

什么时候应该使用 EventEmitter?

[英]When should I use EventEmitter?

I read a lot of things about EventEmitter .我读了很多关于EventEmitter的东西。 But I do not know in which case I need to use it in my Node.js application.但我不知道在哪种情况下我需要在我的 Node.js 应用程序中使用它。

Whenever it makes sense for code to SUBSCRIBE to something rather than get a callback from something. 每当代码对某些东西进行SUBSCRIBE而不是从某些东西获得回调时是有意义的。 The typical use case would be that there's multiple blocks of code in your application that may need to do something when an event happens. 典型的用例是应用程序中存在多个代码块,可能需要在事件发生时执行某些操作。

For example, let's say you are creating a ticketing system. 例如,假设您正在创建一个票务系统。 The common way to handle things might be like this: 处理事情的常用方法可能是这样的:

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

        callback();
    });
}

But now, someone has decided that when a ticket is inserted into the database, you should email the user to let them know. 但是现在,有人决定在将数据库插入数据库时​​,您应该通过电子邮件发送给用户,让他们知道。 That's fine, you can add it to the callback: 没关系,你可以把它添加到回调中:

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

        emailUser(ticket, callback);
    });
}

But now, someone wants to also notify another system that the ticket has been inserted. 但现在,有人还希望通知另一个系统已插入故障单。 Over time, there could be any number of things that should happen when a ticket is inserted. 随着时间的推移,插入故障单时可能会发生任何事情。 So let's change it around a bit: 所以让我们改变一下:

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

        TicketEvent.emit('inserted', ticket);
        callback();
    });
}

We no longer need to wait on all these functions to complete before we notify the user interface. 在通知用户界面之前,我们不再需要等待所有这些功能完成。 And elsewhere in your code, you can add these functions easily: 在代码的其他地方,您可以轻松添加这些功能:

TicketEvent.on('inserted', function(ticket) {
    emailUser(ticket);
});

TicketEvent.on('inserted', function(ticket) {
    notifySlack(ticket);
});

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.当希望将代码库分解为使用类似 pub-sub 的异步模式调用的组件或服务时,使用 Node.js 事件发射器。 However, typically when we talk about pub-sub pattern we refer to distributed decomposed systems.但是,通常当我们谈论 pub-sub 模式时,我们指的是分布式分解系统。 This is not the case here as all components exist in the same code repository and run in the same Node.js runtime.这不是这里的情况,因为所有组件都存在于同一个代码存储库中并在同一个 Node.js 运行时中运行。

Keep in mind that using Node.js Event Emitter does not make our code automatically non-blocking, asynchronous.请记住,使用 Node.js 事件发射器不会使我们的代码自动成为非阻塞、异步的。 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');
  • The EventEmitter should be used when the same event can occur multiple times, or may not occur at all.当同一事件可能发生多次或根本不发生时,应使用 EventEmitter。 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回拨意味着当你准备好时给我打电话

    <\/li>

  • 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.使用回调的 API 只能通知一个特定的回调,而使用 EventEmitter 允许我们为同一事件注册多个侦听器。

    <\/li>

  • Use event emitter if you need to notify the user of a state change.如果您需要通知用户状态更改,请使用事件发射器。

    <\/li>

  • For testing purpose, if you want to make sure a function is called inside a function, emit an event.出于测试目的,如果要确保在函数内部调用函数,请发出事件。

    <\/li><\/ul>"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM