在本教程中我们学习 Node.js 的原生 EvenEmitter 类。学完后你将了解事件、怎样使用 EvenEmitter 以及如何在程序中利用事件。另外还会学习 EventEmitter 类从其他本地模块扩展的内容,并通过一些例子了解背后的原理 ...
![]() 在本教程中我们学习 Node.js 的原生 总之本文涵盖了关于 什么是事件?当今事件驱动的体系结构非常普遍,事件驱动的程序可以产生、检测和响应各种事件。 Node.js 的核心部分是事件驱动的,有许多诸如文件系统( 在事件驱动的编程中,事件(event) 是一个或多个动作的结果,这可能是用户的操作或者传感器的定时输出等。 我们可以把事件驱动程序看作是发布-订阅模型,其中发布者触发事件,订阅者侦听事件并采取相应的措施。 例如,假设有一个服务器,用户可以向其上传图片。在事件驱动的编程中,诸如上传图片之类的动作将会发出一个事件,为了利用它,该事件还会有 1 到 n 个订阅者。 在触发上传事件后,订阅者可以通过向网站的管理员发电子邮件,让他们知道用户已上传照片并对此做出反应;另一个订阅者可能会收集有关操作的信息,并将其保存在数据库中。 这些事件通常是彼此独立的,尽管它们也可能是相互依赖的。 什么是EventEmitter?
大部分的 Node.js 核心 API 都是基于惯用的异步事件驱动的体系结构所实现的,在该体系结构中,某些类型的对象(称为“发射器”)发出已命名事件,这些事件会导致调用 这个类在某种程度上可以描述为发布-订阅模型的辅助工具的实现,因为它可以用简单的方法帮助事件发送器(发布者)发布事件(消息)给 监听器(订阅者)。 创建 EventEmitters话虽如此,但还是先创建一个 创建 EventEmitter 对象先从一个简单的例子开始:创建一个 首先从 const { EventEmitter } = require('events');然后创建一个 const timerEventEmitter = new EventEmitter(); 用这个对象发布事件非常容易: timerEventEmitter.emit("update");前面已经指定了事件名,并把它发布为事件。但是程序没有任何反应,因为还没有侦听器对这个事件做出反应。 先让这个事件每秒重复一次。用 let currentTime = 0;
// 每秒触发一次 update 事件
setInterval(() => {
currentTime++;
timerEventEmitter.emit('update', currentTime);
}, 1000);
通过 timerEventEmitter.on('update', (time) => {
console.log('从发布者收到的消息:');
console.log(`程序已经运行了 ${time} 秒`);
});通过
运行代码将会输出: 从发布者收到的消息: 程序已经运行了 1 秒 从发布者收到的消息: 程序已经运行了 2 秒 从发布者收到的消息: 程序已经运行了 3 秒 ... 如果只在事件首次触发时才需要执行某些操作,也可以用 timerEventEmitter.once('update', (time) => {
console.log('从发布者收到的消息:');
console.log(`程序已经运行了 ${time} 秒`);
});运行这段代码会输出: 从发布者收到的消息: 程序已经运行了 1 秒 EventEmitter 与多个监听器下面创建另一种事件发送器。这是一个计时程序,有三个侦听器。第一个监听器每秒更新一次时间,第二个监听器在计时即将结束时触发,最后一个在计时结束时触发:
先写一个创建这个事件发射器的函数: const countDown = (countdownTime) => {
const eventEmitter = new EventEmitter();
let currentTime = 0;
// 每秒触发一次 update 事件
const timer = setInterval(() => {
currentTime++;
eventEmitter.emit('update', currentTime);
// 检查计时是否已经结束
if (currentTime === countdownTime) {
clearInterval(timer);
eventEmitter.emit('end');
}
// 检查计时是否会在 2 秒后结束
if (currentTime === countdownTime - 2) {
eventEmitter.emit('end-soon');
}
}, 1000);
return eventEmitter;
};这个函数启动了一个每秒钟发出一次 第一个 如果计时没有结束,那么就检查计时是不是离结束还有 2 秒,如果是则发布 向该事件发射器添加一些订阅者: const myCountDown = countDown(5);
myCountDown.on('update', (t) => {
console.log(`程序已经运行了 ${t} 秒`);
});
myCountDown.on('end', () => {
console.log('计时结束');
});
myCountDown.on('end-soon', () => {
console.log('计时将在2秒后结束');
});这段代码将会输出: 程序已经运行了 1 秒 程序已经运行了 2 秒 程序已经运行了 3 秒 计时将在2秒后结束 程序已经运行了 4 秒 程序已经运行了 5 秒 计时结束 扩展 EventEmitter接下来通过扩展 const { EventEmitter } = require('events');
class CountDown extends EventEmitter {
constructor(countdownTime) {
super();
this.countdownTime = countdownTime;
this.currentTime = 0;
}
startTimer() {
const timer = setInterval(() => {
this.currentTime++;
this.emit('update', this.currentTime);
// 检查计时是否已经结束
if (this.currentTime === this.countdownTime) {
clearInterval(timer);
this.emit('end');
}
// 检查计时是否会在 2 秒后结束
if (this.currentTime === this.countdownTime - 2) {
this.emit('end-soon');
}
}, 1000);
}
}可以在类的内部直接使用 创建一个 const myCountDown = new CountDown(5);
myCountDown.on('update', (t) => {
console.log(`计时开始了 ${t} 秒`);
});
myCountDown.on('end', () => {
console.log('计时结束');
});
myCountDown.on('end-soon', () => {
console.log('计时将在2秒后结束');
});
myCountDown.startTimer();运行程序会输出: 程序已经运行了 1 秒 程序已经运行了 2 秒 程序已经运行了 3 秒 计时将在2秒后结束 程序已经运行了 4 秒 程序已经运行了 5 秒 计时结束
myCountDown.on('end-soon', () => {
console.log('计时将在2秒后结束');
});也可以用 myCountDown.addListener('end-soon', () => {
console.log('计时将在2秒后结束');
});EventEmitter 的主要函数eventNames()此函数将以数组形式返回所有活动的侦听器名称: const myCountDown = new CountDown(5);
myCountDown.on('update', (t) => {
console.log(`程序已经运行了 ${t} 秒`);
});
myCountDown.on('end', () => {
console.log('计时结束');
});
myCountDown.on('end-soon', () => {
console.log('计时将在2秒后结束');
});
console.log(myCountDown.eventNames());运行这段代码会输出: [ 'update', 'end', 'end-soon' ] 如果要订阅另一个事件,例如 这个方法不会返回已发布的事件,而是返回订阅的事件的列表。 removeListener()这个函数可以从 const { EventEmitter } = require('events');
const emitter = new EventEmitter();
const f1 = () => {
console.log('f1 被触发');
}
const f2 = () => {
console.log('f2 被触发');
}
emitter.on('some-event', f1);
emitter.on('some-event', f2);
emitter.emit('some-event');
emitter.removeListener('some-event', f1);
emitter.emit('some-event');在第一个事件触发后,由于 f1 被触发 f2 被触发 f2 被触发 An alias for
emitter.off('some-event', f1);removeAllListeners()该函数用于从 const { EventEmitter } = require('events');
const emitter = new EventEmitter();
const f1 = () => {
console.log('f1 被触发');
}
const f2 = () => {
console.log('f2 被触发');
}
emitter.on('some-event', f1);
emitter.on('some-event', f2);
emitter.emit('some-event');
emitter.removeAllListeners();
emitter.emit('some-event');第一个 f1 被触发 f2 被触发 错误处理如果要在 myEventEmitter.emit('error', new Error('出现了一些错误'));
例如在 class CountDown extends EventEmitter {
constructor(countdownTime) {
super();
if (countdownTimer < 2) {
this.emit('error', new Error('countdownTimer 的值不能小于2'));
}
this.countdownTime = countdownTime;
this.currentTime = 0;
}
// ...........
}处理这个错误的方式与其他事件相同: myCountDown.on('error', (err) => {
console.error('发生错误:', err);
});始终对 使用 EventEmitter 的原生模块Node.js 中许多原生模块扩展了 一个典型的例子是 流可以是可读的、可写的,或两者均可。所有流都是 先看一下经典的 Stream 用法: const fs = require('fs');
const writer = fs.createWriteStream('example.txt');
for (let i = 0; i < 100; i++) {
writer.write(`hello, #${i}!\n`);
}
writer.on('finish', () => {
console.log('All writes are now complete.');
});
writer.end('This is the end\n');但是,在写操作和 另一个类是 const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);
ls.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
ls.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
ls.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});当 最后,在进程退出后,将会触发 总结事件驱动的体系结构使我们能够创建高内聚低耦合的系统。事件表示某个动作的结果,可以定义 1个或多个侦听器并对其做出反应。 本文深入探讨了 最后介绍了该类的一些重要函数。 更多编程相关知识,请访问:编程课程!! 以上就是在Node.js中如何使用EventEmitter处理事件?的详细内容,更多请关注模板之家(www.mb5.com.cn)其它相关文章! |
