Nodejs的大部分核心API都是基于异步事件驱动设计的,所有可以分发事件的对象都是EventEmitter类的实例。
大家知道,由于nodejs是单线程运行的,所以nodejs需要借助事件轮询,不断去查询事件队列中的事件消息,然后执行该事件对应的回调函数,有点类似windows的消息映射机制。至于更细的实现环节,可以另行查找资料。
下面介绍EventEmitter的使用。
1、监听事件和分发事件
EventEmitter实例可以使用on
或addListener
监听事件,emit()
方法分发事件,如下所示:
const events = require('events'), EventEmitter = events.EventEmitter, util = require('util'); function myEmiter(){ EventEmitter.call(this); }; util.inherits(myEmiter,EventEmitter);//继承EventEmitter类 const myEmitterIns = new myEmiter(); myEmitterIns.on('data',(o)=>{ console.log('receive the data:'+o.a); });
或者使用class类
class myEmiter extends EventEmitter{}//继承EventEmitter类 const myEmitterIns = new myEmiter(); myEmitterIns.on('data',(o)=>{ console.log('receive the data:'+o.a); }); myEmitterIns.emit('data',{a:1});
执行结果如下:
E:\developmentdocument\nodejsdemo>node event-example.js
receive the data:1
2、向事件监听回调函数传递参数
从上面的例子可以看出,emit()
方法可以传递任意的参数集合给回调函数,需要注意的一点是this关键字指向的是调用emit方法的EventEmiter实例,但在箭头函数中例外,this指向的是全局this,因为箭头函数中的this是在定义时绑定。如下所示:
class myEmiter extends EventEmitter{} const myEmitterIns = new myEmiter(); myEmitterIns.on('data',function(data){ console.log("普通回调函数中this:"); console.log(this); }); myEmitterIns.on('data1',(data1)=>{ console.log("箭头回调函数中this:"); console.log(this); }); myEmitterIns.emit('data',{a:1}); myEmitterIns.emit('data1',{a:1});
执行结果如下:
E:\developmentdocument\nodejsdemo>node event-example.js
普通回调函数中this:
myEmiter {
domain: null,
_events: { data: [Function], data1: [Function] },
_eventsCount: 2,
_maxListeners: undefined }
箭头回调函数中this:
{}
这里讲到箭头函数中的this,就顺便说一下,为什么箭头函数能够实现定义时绑定this,就是因为箭头函数内部根本就没有绑定this的机制,它使用的是外层作用域的this,因此它也不能作为构造函数。
3、事件监听程序的执行顺序
EventEmiter实例可以绑定多个事件,当我们顺序触发这些事件时,EventEmiter会以同步模式执行,既第一个事件处理函数没有完成,便不会触发下一个事件,如下所示:
class myEmiter extends EventEmitter{} const myEmitterIns = new myEmiter(); myEmitterIns.on('data',function(data){ console.time('data事件执行了'); for(var i = 0 ; i< 100000; i++) for(var j = 0 ; j< 100000; j++) ; console.timeEnd('data事件执行了'); }); myEmitterIns.on('data1',(data1)=>{ console.log("data1事件开始执行..."); }); myEmitterIns.emit('data',{a:1}); myEmitterIns.emit('data1',{a:1});
执行结果如下:
E:\developmentdocument\nodejsdemo>node event-example.js
data事件执行了: 4721.401ms
data1事件开始执行...
当然我们可以在回调函数中使用异步操作,例如setTimeout,setImmediate或者process.nextTick()等,从而实现异步的效果,如下所示:
myEmitterIns.on('data',function(data){ setImmediate(()=>{ console.log('data事件执行了...'); }); });
执行结果如下:
E:\developmentdocument\nodejsdemo>node event-example.js
data1事件执行了...
data事件执行了...
4、一次性事件监听
EventEmiter可以使用once监听某个事件,则该事件处理程序只会触发一次,之后emit该事件都会被忽略,因为监听程序被注销了,如下所示:
myEmitterIns.once('one',(data)=>{ console.log(data); }); myEmitterIns.emit('one','this is first call!'); myEmitterIns.emit('one','this is second call!');
执行结果如下:
E:\developmentdocument\nodejsdemo>node event-example.js
this is first call!
从上面的结果看出,'one'事件只执行了一次。
5、移除事件绑定
类似DOM事件监听,EventEmiter也可以移除事件绑定,利用removeListener(eventName,listener)
方法解除某个事件的绑定,因此回调函数listener必须是命名函数,要不然找不到该函数,因为函数是引用型类型,就算函数体是一样,也不是同一个函数,如下所示:
myEmitterIns.on('data',function(e){ console.log(e); }); myEmitterIns.removeListener('data',function(e){ console.log(e); }); myEmitterIns.emit('data','hello data!'); function deal(e){ console.log(e); } myEmitterIns.on('data1',deal); myEmitterIns.removeListener('data1',deal); myEmitterIns.emit('data1','hello data1!');
执行结果如下:
E:\developmentdocument\nodejsdemo>node event-example.js
hello data!
E:\developmentdocument\nodejsdemo>
从执行结果可以看出,data事件使用的是匿名函数,因此没有被移除掉,而data1事件则成功解除绑定了。这里需要注意一点的是emit触发某个事件后,所有跟该事件绑定的回调函数都会被调用,即使你在某个回调函数中使用removeListener函数移除掉另一个回调也没有用,但是随后的事件队列是移除了该回调的。如下所示:
function dealData1(e){ console.log('data事件执行了...A'); } myEmitterIns.on('data',function(e){ console.log(e); myEmitterIns.removeListener('data',dealData1);//这里解除dealData1的绑定 }); myEmitterIns.on('data',dealData1); myEmitterIns.emit('data','data事件执行了...B'); /*执行结果为: data事件执行了...B data事件执行了...A */ //再次触发该事件时,dealData1回调已经被解除绑定了 myEmitterIns.emit('data','data事件执行了...'); //data事件执行了...
另外可以使用removeAllListeners()
解除所有事件的绑定。
6、获取事件监听数量和监听函数
使用emitter.listenerCount(eventName)
函数获取指定事件的监听数量,函数emitter.listeners(eventName)
则可以用来获取指定事件的所有监听函数,使用如下所示:
var cbA = ()=>{}, cbB = ()=>{}; var emitter = new myEmiter(); emitter.on('data',cbA); emitter.on('data',cbB); console.log('emitter实例的data事件绑定了%d个回调函数',emitter.listenerCount('data')); console.log('它们是:',emitter.listeners('data'));
执行结果如下:
E:\developmentdocument\nodejsdemo>node event-example.js
emitter实例的data事件绑定了2个回调函数
它们是: [ [Function: cbA], [Function: cbB] ]
7、获取和设置emitter的最大监听数量
nodejs对同一事件的监听数量建议不宜超过10个,这个可以查看EventEmitter.defaultMaxListeners
属性便可得知,如下所示:
console.log(EventEmitter.defaultMaxListeners);//结果为10个
emitter通过getMaxListeners()
方法获取最大监听数量以及setMaxListeners(n)
方法设置最大监听数量,如下所示:
var cbA = ()=>{}, cbB = ()=>{}; var emitter = new myEmiter(); emitter.setMaxListeners(1); emitter.on('data',cbA); emitter.on('data',cbB); console.log(emitter.getMaxListeners());
执行结果如下:
E:\developmentdocument\nodejsdemo>node event-example.js
emitter的事件最大监听数是:1
(node:6808) Warning: Possible EventEmitter memory leak detected. 2 data listener
s added. Use emitter.setMaxListeners() to increase limit
如上结果所示,如果设置了最大监听数量,则同一事件的监听最好不要超过该最大值,否则很可能发送内存泄漏。
events模块便介绍到这。希望对大家的学习有所帮助,也希望大家多多支持。
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。