概述
NodeJS 事件系统是很多其它NodeJS系统(如net、http(s)等)的基础之一。
事件系统可以定义自己的一套事件系统,并绑定若干处理事件。
事件系统封装在NodeJS的 events
包里,具体API与官方说明参见官方文档。
基本使用姿势
引入 events 包
var events = require('events');
这个 events 是一个类(JS中类即函数),它还有几个成员,其中EventEmitter
这个类跟events类是一样的,这样写的目的是为了跨版本兼容(Node 自身的 API 版本变动)。
events === events.EventEmitter; // true
事件发射器(EventEmitter) 就是一个管理一组事件的对象。
var myEvent = new events(); // create a new event emitter object
触发事件
触发事件使用emitter.emit(eventName: String[, ...argv: Any ]);
来触发,顺便可以在这里传入任意个数、格式不限的参数。
class Person {
constructor (name) {
this.name = name;
}
destroy () {
console.log(this.name, 'dead');
}
}
var Riki = new Person('Riki');
myEvent.on('go-die', person => person.destroy());
myEvent.emit('go-die', Riki); // Riki dead
为事件绑定监听器
有的人喜欢称呼这个动作为”绑定事件”,然而这个说法并不好,因为每个事件名可以对应若干监听器(listener),准确的说应该是为事件绑定监听器,或者是将监听器绑定到事件上。
一次性监听器
一次性监听器在绑定后,只能被触发一次,触发后就自我销毁,下次再触发同样的事件也不会再响应。
使用emitter.once(eventName: String, listener: Function);
将listener绑定到对应的事件上。
myEvent.once('init', () => { sum = 0; });
myEvent.emit('init'); // sum == 0;
sum ++; // sum == 1;
myEvent.emit('init'); // sum == 1;
常态监听器
与一次性监听器不同,常态监听器在触发后不会销毁,每一次触发事件时都会调用。
使用emitter.on(eventName: String, listener: Function);
将listener绑定到对应的事件上。
myEvent.on('init', () => { sum = 0; });
myEvent.emit('init'); // sum == 0;
sum ++; // sum == 1;
myEvent.emit('init'); // sum == 0;
监听器列表
可以在一个事件上绑定若干监听器,每一个事件都会对应一个监听器列表,当事件触发时依次阻塞执行:按照监听器的顺序,一个一个执行,当上一个监听器返回结果后,再执行下一个监听器(同步: Synchronous)。
常态监听器与一次性监听器可以混合绑定到同一个事件上。
使用on, once
绑定顺序与执行顺序一致:先绑定的先执行,后绑定的后执行。
利用prependListener、prependOnceListener分别对应on、once,区别在于prepend*方法将监听器插入到监听器列表的开头,优先执行。
异步监听器
监听器列表的同步阻塞执行是不可改变的,要实现异步的监听器只能用异步方法做一些妥协。
- setImmediate 立即执行
- setInterval 周期循环执行
- setTimeout 延迟执行
myEvent.on('fire', () => {
setTimeout(() => {
console.log('fired');
}, 1000); // 秒射
});
myEvent.emit('fire'); // 1秒后输出 fired
移除监听器
清空所有监听器
使用emitter.removeAllListeners()
方法来清空所有事件的所有监听器。
清空若干事件的所有监听器
使用emitter.removeAllListeners([eventNames])
。
移除某事件的某监听器
如果你还保存着监听器的引用,那么你可以使用 emitter.removeListener(eventName, listener)
来精确移除监听器。
如果你没有保存监听器的引用,其实事件发射器已经帮你保存好了(不然它怎么调用呢)。
使用emitter._events[eventNames]
来获取事件对应的监听器(列表)。
完整样例:事件驱动的生物种群模拟
var events = require('events');
var myEvent = new events();
class Creature {
constructor() {
this.age = 0;
this.alive = true;
}
}
// 主循环
myEvent.on('start', cluster => {
setInterval(() => {
myEvent.emit('time', cluster);
}, 1000);
});
// 报告生存
myEvent.on('time', cluster => {
var nowAlive = cluster.filter(v => v.alive);
console.log(`new year come! now alive ${nowAlive.length} creatures`);
});
// 成长或死亡
myEvent.on('time', cluster => {
cluster.filter(v => v.alive).forEach((v, i) => {
if (~~(Math.random() * 10) < 5) myEvent.emit('dead', v);
else myEvent.emit('grow', v);
});
});
// 繁衍
myEvent.on('time', cluster => {
var times = cluster.filter(v => v.alive).length;
for (var i = 0; i < times; i++) cluster.push(new Creature());
})
// 检查终止
myEvent.on('time', cluster => {
if (cluster.filter(v => v.alive).length == 0) myEvent.emit('end', cluster);
});
// 结算
myEvent.on('end', cluster => {
console.log(`all ${cluster.length} creatures dead`);
console.log(`dead age summary:${cluster.map(v => v.age).reduce((a, b) => a + b, 0)}`);
process.exit(0);
})
// 成长
myEvent.on('grow', creature => { creature.age++; });
// 死亡
myEvent.on('dead', creature => { creature.alive = false; });
var cluster = [new Creature(), new Creature()];
myEvent.emit('start', cluster);
EventEmitter 内部结构
属性
以下划线开头的属性通常是库内部使用,不希望对外开放的属性,因此最好不要试图去修改这些属性。
console.log(myEvent);
/**
EventEmitter {
domain:
Domain {
domain: null,
_events: { error: [Function] },
_eventsCount: 1,
_maxListeners: undefined,
members: [] },
_events: {},
_eventsCount: 0,
_maxListeners: undefined
}
*/
可以看到一个事件发射器实例中有domain, _events, _eventsCount, _maxListeners
这几个可枚举属性。
domain是域,在原先的版本中是独立于events包的一个包,最近NodeJS将domain并入events,并且废弃单独的domain包,在官方文档中 domain包的稳定级别已经降为0,很有可能在下一个大版本中就会消失。
主要是domain的功能基本就是events功能的子集,因此domain就被并入了events。大体上一个domain也就是一个EventEmitter。
_events
_events
是一个关联数组(eventName -> listener([])
)。
比如在事件a上绑定了两个监听器,在b上绑定了一个监听器时:
_events: {
a: [ [Function], [Function] ],
b: [Function]
}
一旦某事件的监听器数量超过1个,便会变成监听器列表。
_eventsCount
代表一共有多少事件是有监听器的。
使用时最好使用emitter.eventNames().length
代替。
_maxListeners
代表每个事件中最大的监听器数量。
说是这么说,但当真的超过这个数量的时候,系统只会 console.log 一个 warning 提示你监听器数量超了。
多出来的监听器依然能够正确地插入监听器列表,并正确执行。
getMaxListeners()
与 setMaxListeners(n)
是 _maxListeners
属性的 getter 与 setter。
最后
以上就是老迟到硬币为你收集整理的NodeJS 事件系统详解基本使用姿势完整样例:事件驱动的生物种群模拟EventEmitter 内部结构的全部内容,希望文章能够帮你解决NodeJS 事件系统详解基本使用姿势完整样例:事件驱动的生物种群模拟EventEmitter 内部结构所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复