详解Node.js:events事件模块


Posted in Javascript onNovember 24, 2016

Nodejs的大部分核心API都是基于异步事件驱动设计的,所有可以分发事件的对象都是EventEmitter类的实例。

大家知道,由于nodejs是单线程运行的,所以nodejs需要借助事件轮询,不断去查询事件队列中的事件消息,然后执行该事件对应的回调函数,有点类似windows的消息映射机制。至于更细的实现环节,可以另行查找资料。

下面介绍EventEmitter的使用。

1、监听事件和分发事件

EventEmitter实例可以使用onaddListener监听事件,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模块便介绍到这。希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript+css 网页每次加载不同样式的实现方法
Dec 27 Javascript
Extjs TimeField 显示正常时间格式的代码
Jun 28 Javascript
$.getJSON在IE下失效的原因分析及解决方法
Jun 16 Javascript
JavaScript的setAttribute兼容性问题解决方法
Nov 11 Javascript
在JS中如何调用JSP中的变量
Jan 22 Javascript
使用jsonp完美解决跨域问题
Nov 27 Javascript
javascript实现回到顶部特效
May 06 Javascript
利用原生JS实现data方法示例代码
May 28 Javascript
javascript设计模式 ? 建造者模式原理与应用实例分析
Apr 10 Javascript
Vue 中使用lodash对事件进行防抖和节流操作
Jul 26 Javascript
针对Vue路由history模式下Nginx后台配置操作
Oct 22 Javascript
Vue组件化(ref,props, mixin,.插件)详解
May 15 Vue.js
详解微信小程序开发之下拉刷新 上拉加载
Nov 24 #Javascript
微信小程序-消息提示框实例
Nov 24 #Javascript
jQuery实现的模拟弹出窗口功能示例
Nov 24 #Javascript
基于JavaScript实现Tab选项卡切换效果
Nov 24 #Javascript
javascript实现获取图片大小及图片等比缩放的方法
Nov 24 #Javascript
网站发布后Bootstrap框架引用woff字体无法正常显示的解决方法
Nov 24 #Javascript
Javascript 数组去重的方法(四种)详解及实例代码
Nov 24 #Javascript
You might like
php实现快速排序法函数代码
2012/08/27 PHP
深入php list()函数的详解
2013/06/05 PHP
解析左右值无限分类的实现算法
2013/06/20 PHP
PHP-Fcgi下PHP的执行时间设置方法
2013/08/02 PHP
php实现cookie加密的方法
2015/03/10 PHP
PHP表单提交后引号前自动加反斜杠的原因及三种办法关闭php魔术引号
2015/09/30 PHP
PHP5.5迭代生成器用法实例详解
2016/03/16 PHP
PHP实现的超长文本分页显示功能示例
2018/06/04 PHP
理解JSON:3分钟课程
2011/10/28 Javascript
多个checkbox被选中时如何判断是否有自己想要的
2014/09/22 Javascript
js实现上传图片及时预览
2016/05/07 Javascript
微信+angularJS的SPA应用中用router进行页面跳转,jssdk校验失败问题解决
2016/09/09 Javascript
谈谈jQuery之Deferred源码剖析
2016/12/19 Javascript
React学习之事件绑定的几种方法对比
2017/09/24 Javascript
JavaScript Date对象应用实例分享
2017/10/30 Javascript
node链接mongodb数据库的方法详解【阿里云服务器环境ubuntu】
2019/03/07 Javascript
页面内锚点定位及跳转方法总结(推荐)
2019/04/24 Javascript
解决node.js含有%百分号时发送get请求时浏览器地址自动编码的问题
2019/11/20 Javascript
微信小程序文章详情功能完整实例
2020/06/03 Javascript
[03:48]DOTA2完美大师赛主赛事第二日精彩集锦
2017/11/24 DOTA
python的dict,set,list,tuple应用详解
2014/07/24 Python
python异常和文件处理机制详解
2016/07/19 Python
利用python在大量数据文件下删除某一行的例子
2019/08/21 Python
Python计算两个矩形重合面积代码实例
2019/09/16 Python
django haystack实现全文检索的示例代码
2020/06/24 Python
python利用opencv保存、播放视频
2020/11/02 Python
详解快速开发基于 HTML5 网络拓扑图应用
2018/01/08 HTML / CSS
Omio西班牙:全欧洲低价大巴、火车和航班搜索和比价
2017/02/11 全球购物
连卡佛中国官网:Lane Crawford中文站
2018/01/27 全球购物
初中校园之声广播稿
2014/01/15 职场文书
公证委托书模板
2014/04/03 职场文书
投资合作协议书
2014/04/17 职场文书
党员反对四风问题思想汇报
2014/09/12 职场文书
小学教师2014年度工作总结
2014/12/03 职场文书
Pillow图像处理库安装及使用
2022/04/12 Python
win10电脑关机快捷键是哪个 win10快速关机的几种方法
2022/08/14 数码科技