【发布时间】:2016-12-17 07:06:15
【问题描述】:
我读了很多关于EventEmitter 的东西。但我不知道在哪种情况下我需要在我的 Node.js 应用程序中使用它。
【问题讨论】:
标签: javascript node.js eventemitter
我读了很多关于EventEmitter 的东西。但我不知道在哪种情况下我需要在我的 Node.js 应用程序中使用它。
【问题讨论】:
标签: javascript node.js eventemitter
只要代码订阅某事而不是从某事获得回调是有意义的。典型的用例是您的应用程序中有多个代码块,当事件发生时可能需要执行一些操作。
例如,假设您正在创建一个票务系统。处理事情的常见方式可能是这样的:
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);
});
【讨论】:
Node.js 事件发射器用于将代码库分解为 使用类似 pub-sub 的异步模式调用的组件或服务。 然而,通常当我们谈论 pub-sub 模式时,我们指的是分布式分解系统。此处并非如此,因为所有组件都存在于同一个代码存储库中并在同一个 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');
【讨论】: