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模块便介绍到这。

Node.js:events事件模块的更多相关文章

  1. 利用Node.js的Net模块实现一个命令行多人聊天室

    1.net模块基本API 要使用Node.js的net模块实现一个命令行聊天室,就必须先了解NET模块的API使用.NET模块API分为两大类:Server和Socket类.工厂方法. Server类 ...

  2. Node.js的Formidable模块的使用

    今天总结了下Node.js的Formidable模块的使用,下面做一些简要的说明. 1)     创建Formidable.IncomingForm对象 var form = new formidab ...

  3. Node.js的net模块

    net模块提供了一个异步网络包装器,用于TCP网络编程,它包含了创建服务器和客户端的方法 创建TCP服务器 net.createServer方法 创建客户端去连接服务器 net.connect方法 简 ...

  4. node.js中express模块创建服务器和http模块客户端发请求

    首先下载express模块,命令行输入 npm install express 1.node.js中express模块创建服务端 在js代码同文件位置新建一个文件夹(www_root),里面存放网页文 ...

  5. node.js中ws模块创建服务端和客户端,网页WebSocket客户端

    首先下载websocket模块,命令行输入 npm install ws 1.node.js中ws模块创建服务端 // 加载node上websocket模块 ws; var ws = require( ...

  6. node.js中net模块创建服务器和客户端(TCP)

    node.js中net模块创建服务器和客户端 1.node.js中net模块创建服务器(net.createServer) // 将net模块 引入进来 var net = require(" ...

  7. node.js使用mysql模块的坑

      之前用node.js写的订餐系统,很容易挂掉,一直也没想去解决它.今天看了一下,试了试,原因是在连接数据库的时候没有对error事件进行处理,导致程序一直挂在那里,需要重启服务才能正常使用.   ...

  8. node.js的http模块的基础 学到的东西

    node.js的http模块的基础 学到的东西 其中客户端:我们在node.js中如果要请求服务端中的js或者其他脚本的话要使用http.request()方法他会返回http.ClientReque ...

  9. Node.js入门:模块机制

    CommonJS规范      早在Netscape诞生不久后,JavaScript就一直在探索本地编程的路,Rhino是其代表产物.无奈那时服务端JavaScript走的路均是参考众多服务器端语言来 ...

  10. 理解Node.js的事件轮询

    前言 总括 : 原文地址:理解Node.js的事件轮询 Node小应用:Node-sample 智者阅读群书,亦阅历人生 正文 Node.js的两个基本概念 Node.js的第一个基本概念就是I/O操 ...

随机推荐

  1. 【.net 深呼吸】细说CodeDom(3):命名空间

    在上一篇文章中,老周介绍了表达式和语句,尽管老周没有把所有的内容都讲一遍,但相信大伙至少已经掌握基本用法.在本文中,咱们继续探讨 CodeDom 方面的奥秘,这一次咱们聊聊命名空间. 在开始之前,老周 ...

  2. wepack+sass+vue 入门教程(一)

    一.安装node.js node.js是基础,必须先安装.而且最新版的node.js,已经集成了npm. 下载地址 node安装,一路按默认即可. 二.全局安装webpack npm install ...

  3. 参考bootstrap中的popover.js的css画消息弹框

    前段时间小颖的大学同学给小颖发了一张截图,图片类似下面这张图: 小颖当时大概的给她说了下,其实小颖也不知道上面那个三角形怎么画嘻嘻,给她说了DOM结构,具体的css让她自己百度,今天小颖自己参考boo ...

  4. 浏览器中用JavaScript获取剪切板中的文件

    本文转自我的个人网站  , 原文地址:http://www.zoucz.com/blog/2016/01/29/get-file-from-clipboard/  ,欢迎前往交流讨论 在网页上编辑内容 ...

  5. jQuery.Ajax IE8 无效(CORS)

    今天在开发的时候,遇到一个问题,$.get()在 IE8 浏览器不起作用,但 Chrome,Firefox 却是可以的,网上资料很多,最后发现是 IE8 默认不支持 CORS 请求,需要手动开启下: ...

  6. PHP之时间和日期函数

    // 时间日期函数 Time <?php date_default_timezone_set('UTC'); // 获取当前时间的时间戳 $time0 = mktime(); $time1 = ...

  7. [原创]关于Hibernate中的级联操作以及懒加载

    Hibernate: 级联操作 一.简单的介绍 cascade和inverse (Employee – Department) Casade用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似 ...

  8. 移动BPM解决方案分享

    畅通开放  无边界的渠道 效率倍增  更高效的处理方式 即时共享  更强大的决策能力 各种终端应用 帮您实现:新任务通知.任务预警.催办.任务审批.任何数据汇总提醒消息通知...... 短信 客户端: ...

  9. 实现php连接memcached

    准备工作: 实现lnmp环境 给php添加模块,so库 下载扩展包:memcache-2.2.5.tgz wget http://pecl.php.net/get/memcache-2.2.5.tgz

  10. Vim使用

    模式 ESC\Ctrl+c:退出编辑模式 ZZ\wq:命令模式下保存退出 编辑 i:进入编辑模式 I:转到行首非空字符开始编辑 s:删除当前字符进入编辑模式 a:从当前字符后开始编辑 A:从当前行末非 ...