跟我学Nodejs(二)--- Node.js事件模块


Posted in NodeJs onMay 21, 2014

简介及资料

http://nodejs.org/api/events.html

http://www.infoq.com/cn/articles/tyq-nodejs-event

    events是node.js 最重要的模块,events模块只提供了一个对象events.EventEmitter,EventEmitter 的核心是事件发射与事件监听器。

    Node.js中大部分的模块,都继承自Event模块。

    与DOM树上事件不同,不存在事件冒泡、逐层捕获等行为。

    EventEmitter 支持若干个事件监听器。当事件发射时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。

 

如何访问:

require('events');

emitter.on(event, listener)

 
/*
    调用events模块,获取events.EventEmitter对象
*/
var EventEmitter = require('events').EventEmitter;   
var ee = new EventEmitter();
/*
    EventEmitter.on(event, listener) 为事件注册一个监听
    参数1:event  字符串,事件名
    参数2:回调函数
*/
ee.on('some_events', function(foo, bar) {
    console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );
});
console.log('第一轮');
ee.emit('some_events', 'Wilson', 'Zhong');
console.log('第二轮');
ee.emit('some_events', 'Wilson', 'Z');
EventEmitter.on(event, listener) 示例源码

emitter.emit(event, [arg1], [arg2], [...])

 
var EventEmitter = require('events').EventEmitter;   
var ee = new EventEmitter();
ee.on('some_events', function(foo, bar) {         
    console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );
});
/*
    EventEmitter.emit(event, [arg1], [arg2], [...])   触发指定事件
    参数1:event  字符串,事件名
    参数2:可选参数,按顺序传入回调函数的参数
    返回值:该事件是否有监听
*/
var isSuccess = ee.emit('some_events', 'Wilson', 'Zhong');
ee.on('some_events', function(foo, bar) {         
    console.log("第2个监听事件,参数foo=" + foo + ",bar="+bar );
});
ee.emit('some_events', 'zhong', 'wei');
var isSuccess2 = ee.emit('other_events', 'Wilson', 'Zhong');
console.log(isSuccess);
console.log(isSuccess2);
emitter.emit(event, [arg1], [arg2], [...]) 示例源码

示例进行了三次触发事件操作,其中some_events注册了监听,调用时emit函数会返回一个true,而other_events并没有注册监听,emit函数会返回一个false,表示该事件没有监听;当然也可以不用管这个返回值!

emitter.once(event, listener)

 
var EventEmitter = require('events').EventEmitter;   
var ee = new EventEmitter();
/*
    EventEmitter.once(event, listener)  为事件注册一次性监听,触发一次后移除监听
    参数1:event  字符串,事件名
    参数2:回调函数
*/
ee.once('some_events', function(foo, bar) {
    console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );
});
console.log('第一轮');
ee.emit('some_events', 'Wilson', 'Zhong');
console.log('第二轮');
var isSuccess =  ee.emit('some_events', 'Wilson', 'Zhong');
console.log(isSuccess);
emitter.once(event, listener) 示例源码

    从上面示例代码执行结果可以看出,用emitter.once给some_events注册一个监听后,分两轮调用emitter.emit触发,第二轮会返回false;这表示用emitter.once注册监听和用前面讲的emitter.on注册监听略有不同,

    emitter.once注册监听是一次性监听,当触发一次后,会移除该监听!当然,从名字上就看就比较明显了^_^!

emitter.removeListener(event, listener)

 先来看一个失败的场景~~~

 
var EventEmitter = require('events').EventEmitter;   
var ee = new EventEmitter();
ee.on('some_events', function(foo, bar) {
    console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );
});
/*
    看到API中removeListener移除方法时,以为应该是这样
    但是结果^_^!!!!!
*/
ee.removeListener('some_events', function(){
    console.log('成功移除事件some_events监听!');        
});
console.log('第一轮');
ee.emit('some_events', 'Wilson', 'Zhong');
emitter.removeListener(event, listener) 示例失败场景源码

    当我用emitter.on给some_events注册了一个监听后,我用emiiter.removeListener移除some_events的监听,随后再调用emitter.emit去触发,最后发现不是按我想像的在进行!为什么呢?

    我理所当然的认为emiiter.removeListener第二个参数是个回调函数,API还是要认真看清楚啊!!!

下面再看个成功的场景~~~

 
var EventEmitter = require('events').EventEmitter;   
var ee = new EventEmitter();
var listener = function(foo,bar)
{
    console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );
}
var listener2= function(foo,bar)
{
    console.log("第2个监听事件,参数foo=" + foo + ",bar="+bar );
}
var listener3= function(foo,bar)
{
    console.log("第3个监听事件,参数foo=" + foo + ",bar="+bar );
}
ee.on('some_events', listener);
ee.on('some_events', listener2);
ee.on('some_events', listener3);
/*
    EventEmitter.removeListener(event, listener)  移除指定事件的监听器
    注意:该监听器必须是注册过的
    PS:上一个例子之后以会失败,很大原因就是忽略了监听器,理所当然的认为传个事件名就OK了,所以就悲剧了!
*/
ee.removeListener('some_events', listener);
ee.removeListener('some_events', listener3);
ee.emit('some_events', 'Wilson', 'Zhong');
emitter.removeListener(event, listener) 示例成功场景源码

    我用示例中写法,给some_events添加了三个监听,又移除了第一个和第三个监听,最后再用emitter.emit触发some_events,输出结果不难发现,用emitter.removeListener移除的第一个和第三个监听都没有再起作用,

    想当然是害人地,原来emitter.removeListener的第二个参数是要移除的监听,而非移除成功后的回调函数……^_^!

emitter.removeAllListeners([event])

emitter.removeListener用过了,但一个事件可以有多个监听,需要全部移除时,一个个移除明显不是愉快的做法,不符合偷懒的天性!

让我们来体验一下emitter.removeAllListeners带来的便捷!

 
var EventEmitter = require('events').EventEmitter;   
var ee = new EventEmitter();
var listener = function(foo,bar)
{
    console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );
}
var listener2= function(foo,bar)
{
    console.log("第2个监听事件,参数foo=" + foo + ",bar="+bar );
}
ee.on('some_events', listener);
ee.on('some_events', listener2);
ee.on('other_events',function(foo,bar)
{
    console.log("其它监听事件,参数foo=" + foo + ",bar="+bar );
});
/*
    EventEmitter.removeAllListeners([event])   移除(批定事件)所有监听器
    参数1:可选参数,event  字符串,事件名
*/
ee.removeAllListeners('some_events');
ee.emit('some_events', 'Wilson', 'Zhong');
ee.emit('other_events', 'Wilson', 'Zhong');
emitter.removeAllListeners 传入事件名参数示例源码

    看看上面的执行结果,你会发现给some_events注册了两个监听;给other_events注册了一个监听;我调用emitter.removeAllListeners传了some_events事件名;

    最后使用emitter.on函数触发some_events和other_events两个事件,最后发现some_events注册的两个监听都不存在,而other_events注册的监听还存在;

    这表示当 emitter.removeAllListeners传用事件名作为参数时,为移除传入事件名的所有监听,而不会影响其它事件监听!

emitter.removeAllListeners可以不传用事件名参数;直接执行

 
var EventEmitter = require('events').EventEmitter;   
var ee = new EventEmitter();
var listener = function(foo,bar)
{
    console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );
}
var listener2= function(foo,bar)
{
    console.log("第2个监听事件,参数foo=" + foo + ",bar="+bar );
}
ee.on('some_events', listener);
ee.on('some_events', listener2);
ee.on('other_events',function(foo,bar)
{
    console.log("其它监听事件,参数foo=" + foo + ",bar="+bar );
});
/*
    EventEmitter.removeAllListeners([event])   移除(批定事件)所有监听器
    参数1:可选参数,event  字符串,事件名
*/
ee.removeAllListeners();
ee.emit('some_events', 'Wilson', 'Zhong');
ee.emit('other_events', 'Wilson', 'Zhong');
emitter.removeAllListeners 不传参数示例源码

    示例代码和传入参数时几乎一样,只是在调用emitter.removeAllListeners并没有传入指定事件名;

    运行结果会发现some_events和other_events所有监听都不存在了,它会移除所有监听!(比较暴力的方法一般要慎用~~)

emitter.listeners(event)

跟我学Nodejs(二)--- Node.js事件模块

var EventEmitter = require('events').EventEmitter;   
var ee = new EventEmitter();
var listener = function(foo,bar)
{
    console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );
}
var listener2= function(foo,bar)
{
    console.log("第2个监听事件,参数foo=" + foo + ",bar="+bar );
}
ee.on('some_events', listener);
ee.on('some_events', listener2);
ee.on('other_events',function(foo,bar)
{
    console.log("其它监听事件,参数foo=" + foo + ",bar="+bar );
});
/*
    EventEmitter.listeners(event)   //返回指定事件的监听数组
    参数1:event  字符串,事件名    
*/
var listenerEventsArr = ee.listeners('some_events');
console.log(listenerEventsArr.length)
for (var i = listenerEventsArr.length - 1; i >= 0; i--) {
    console.log(listenerEventsArr[i]); 
};
emitter.listeners(event) 示例源码

    给some_events注册两个监听,调用emitter.listeners函数,传入some_events事件名,接收函数返回值;

    从结果可以看出,返回值接收到some_events所有注册监听的集合!

emitter.setMaxListeners(n)

 一个事件可以添加多个监听是没错,但Nodejs默认最大值是多少呢?

 
var EventEmitter = require('events').EventEmitter;   
var ee = new EventEmitter();
/*
     给EventEmitter 添加11个监听
*/
for (var i = 10; i >= 0; i--) {
    ee.on('some_events',function()
    {
        console.log('第'+ (i +1) +'个监听');
    });
};
添加N个监听示例源码

上面示例中我用个循环给some_events添加11个监听,执行代码,发现warning信息出现,并且提示的比较详细了,需要用emitter.setMaxListeners()去提升限值

跟我学Nodejs(二)--- Node.js事件模块

var EventEmitter = require('events').EventEmitter;   
var ee = new EventEmitter();
/*
    EventEmitter.setMaxListeners (n)   给EventEmitter设置最大监听
    参数1: n 数字类型,最大监听数    超过10个监听时,不设置EventEmitter的最大监听数会提示:
    (node) warning: possible EventEmitter memory leak detected. 11 listeners added.
     Use emitter.setMaxListeners() to increase limit.
    设计者认为侦听器太多,可能导致内存泄漏,所以存在这样一个警告
*/
ee.setMaxListeners(15);
/*
     给EventEmitter 添加11个监听
*/
for (var i = 10; i >= 0; i--) {
    ee.on('some_events',function()
    {
        console.log('第'+ (i +1) +'个监听');
    });
};
emitter.setMaxListeners 示例源码

    当我调用emitter.setMaxListeners传入15时,执行代码,warning信息不再出现;

    emitter.setMaxListeners的作用是给EventEmitter设置最大监听数,感觉一般是不需要设置这个值,10个还不够用的情况应该是比较少了!

    设计者认为侦听器太多会导致内存泄漏,所有就给出了一个警告!

其它...

 用的比较少的就不详细说了

EventEmitter.defaultMaxListeners

    EventEmitter.defaultMaxListeners功能与setMaxListeners类似,
    给所有EventEmitter设置最大监听
    setMaxListeners优先级大于defaultMaxListeners

EventEmitter.listenerCount(emitter, event)

    返回指定事件的监听数

 特殊的事件Error

    引用自Node.js开发指南:EventEmitter 定义了一个特殊的事件 error,它包含了“错误”的语义,我们在遇到 异常的时候通常会发射 error 事件。当 error 被发射时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并打印调用栈。我们一般要为会发射 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。

事件的继承

    以后归到util里再讲一下吧,有兴趣的可以自已看看 http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor

NodeJs 相关文章推荐
nodejs实用示例 缩址还原
Dec 28 NodeJs
基于 Docker 开发 NodeJS 应用
Jul 30 NodeJs
nodejs中转换URL字符串与查询字符串详解
Nov 26 NodeJs
nodejs修复ipa处理过的png图片
Feb 17 NodeJs
在windows上用nodejs搭建静态文件服务器的简单方法
Aug 11 NodeJs
基于NodeJS+MongoDB+AngularJS+Bootstrap开发书店案例分析
Jan 12 NodeJs
nodejs处理图片的中间件node-images详解
May 08 NodeJs
nodejs后台集成ueditor富文本编辑器的实例
Jul 11 NodeJs
nodejs操作mongodb的增删改查功能实例
Nov 09 NodeJs
Nodejs调用Dll模块的方法
Sep 17 NodeJs
nodejs读取本地中文json文件出现乱码解决方法
Oct 10 NodeJs
nodejs中使用worker_threads来创建新的线程的方法
Jan 22 NodeJs
跟我学Nodejs(一)--- Node.js简介及安装开发环境
May 20 #NodeJs
Nodejs使用mysql模块之获得更新和删除影响的行数的方法
Mar 18 #NodeJs
用nodejs实现PHP的print_r函数代码
Mar 14 #NodeJs
nodejs读取memcache示例分享
Jan 02 #NodeJs
利用NodeJS和PhantomJS抓取网站页面信息以及网站截图
Nov 18 #NodeJs
NodeJS的url截取模块url-extract的使用实例
Nov 18 #NodeJs
NodeJS url验证(url-valid)的使用方法
Nov 18 #NodeJs
You might like
oracle资料库函式库
2006/10/09 PHP
生成ubuntu自动切换壁纸xml文件的php代码
2010/07/17 PHP
phpMyAdmin 链接表的附加功能尚未激活的问题
2010/08/01 PHP
PHP rawurlencode与urlencode函数的深入分析
2013/06/08 PHP
使用PHP实现Mysql读写分离
2013/06/28 PHP
php中实现精确设置session过期时间的方法
2014/07/17 PHP
PHP生成plist数据的方法
2015/06/16 PHP
thinkphp5框架调用其它控制器方法 实现自定义跳转界面功能示例
2019/07/03 PHP
php的对象传值与引用传值代码实例讲解
2021/02/26 PHP
XML的代替者----JSON
2007/07/21 Javascript
javascript 语法基础 想学习js的朋友可以看看
2009/12/16 Javascript
JavaScript学习笔记之ES6数组方法
2016/03/25 Javascript
jquery层级选择器(匹配父元素下的子元素实现代码)
2016/09/05 Javascript
JS命令模式例子之菜单程序
2016/10/10 Javascript
JavaScript实现两个select下拉框选项左移右移
2017/03/09 Javascript
深入理解Webpack 中路径的配置
2017/06/17 Javascript
使用vue-router beforEach实现判断用户登录跳转路由筛选功能
2018/06/25 Javascript
vue-router路由懒加载的实现(解决vue项目首次加载慢)
2018/08/28 Javascript
基于jQuery实现可编辑的表格
2019/12/11 jQuery
理解JavaScript中的对象
2020/08/25 Javascript
python实现去除下载电影和电视剧文件名中的多余字符的方法
2014/09/23 Python
python处理图片之PIL模块简单使用方法
2015/05/11 Python
python开发之字符串string操作方法实例详解
2015/11/12 Python
Python中内建函数的简单用法说明
2016/05/05 Python
Python的Flask框架中使用Flask-Migrate扩展迁移数据库的教程
2016/06/14 Python
Django2.1.3 中间件使用详解
2018/11/26 Python
Python中PyQt5/PySide2的按钮控件使用实例
2019/08/17 Python
python3实现简单飞机大战
2020/11/29 Python
结婚典礼证婚词
2014/01/11 职场文书
《跨越百年的美丽》教学反思
2014/02/11 职场文书
高一学生评语大全
2014/04/25 职场文书
建筑投标担保书
2014/05/20 职场文书
2014年企业党支部工作总结
2014/12/04 职场文书
2015年教师节活动总结
2015/03/20 职场文书
初中英语教师个人工作总结2015
2015/07/21 职场文书
HR在给员工开具离职证明时,需要注意哪些问题?
2019/07/03 职场文书