第7章 Node.js EventEmitter

Node.js 所有的异步I/O操作在完成是都会发送一个事件到事件队列。
Node.js 里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时触发一个事件,一个fs.resaStream对象会在文件被打开的时候触发一个事件。所有这些产生事件的对象都是events.EventEmitter的实例。

7-1EventEmitter类

events模块只提供了一个对象:events.EventEmitter。
EventEmitter的核心就是事件触发事件监听器功能的封装。
可以通过require('events')来访问该模块

//引入 events 模块
var events = require('events');

//创建eventEmitter对象
var eventEmitter  = new events.EventEmitter();

EventEmitter 对象如果在实例化时发生错误,会触发error事件。
当添加新的监听器时,newListener 事件会触发。
当监听器被移除时,removeListener事件会被触发。
下面咱们来看一个例子:

//event.js 文件
var EventEmitter = require('events').EventEmitter;
var event = new EventEmitter();
event.on('some_event', function() {
  console.log('some_event 事件触发');
});
setTimeOut(function() {
  event.emit('some_event')
}, 1000);

执行结果:

node event.js

some_event 事件触发

运行这段代码,1秒后控制台输出了some_evnet 事件触发
其原理是event对象注册了事件some_event的一个监听器,
然后我们通过setTimeout在1秒后向event对象发送事件some_event,此时会调用some_event的监听器。

EventEmitter的每个事件有一个事件名若干个参数组成。
事件名:是一个字符串,有一定的语义化
对于每个事件,EventEmitter支持若干个事件监听器。
当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递
那么我们来看一个例子:

//event1.js
var events = require('events');
var emitter =  new events.EventEmitter();
emitter.on('someEvent',function(arg1,arg2){
  console.log('listener1',arg1,arg2)
});
emitter.on('someEvent',function(arg1,arg2){
  console.log('listener2',arg1,arg2)
});
emitter.emit('someEvent','参数1','参数2');

运行结果:

node event1.js
listener1 参数1 参数2
listener2 参数1 参数2

从上面这个例子来看:
emitter为事件someEvent注册了两个事件监听器,
然后出发了someEvent事件。
EventEmitter提供了多个属性,如onemit

  • on函数:用于绑定事件函数
  • emit:用于触发一个事件
    那么EventEmitter都有哪些属性呢?
7-2方法
1.addListener(event,listener)   //为指定事件添加一个监听器到监听器数组的尾部
2.on(event,listener) //为指定事件注册一个监听器,接受一个字符串event和一个回调函数
server.on('connection',function(stream){
  console.log('someone connected!');
})
3.once(event,listener) //未指定事件注册一个单次监听器,即 监听器最多只会触发一次,出发后立即解除该监听器
4.removeListener(event,listener) //移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器,它会接受两个参数,第一个是事件名称,第二个是回调函数名称
var callback = function(stream){
  console.log('someone connected')
}
server.on('connection',callback);
server.removeListener('connection',callback);
5.removeAllListener([event]) //移除所有事件的所有监听,如果指定事件,则移除该事件的所有监听
6.setMaxListener(n) //默认10个 设置数字可以提高监听器的默认限制数量
7.listeners(event) //返回指定事件的监听数组
8.emit(event,[arg1],[arg2],...) //按监听器的书序执行每个监听器,如果事件有注册监听返回true,否者返回false
7-3 类方法
1.listenerCount(emitter,event)  //返回指定事件的监听器数量
events.EventEmitter.listenerCount(emitter,eventName) //已废弃,不推荐
events.emitter.listenerCount(eventName) //推荐
7-4 事件
1.newListener //该事件在添加新监听器时被触发
2.removeListener //从指定监听器数组中删除一个监听器,需要注意的是,此操作将会改变处于被删监听器之后的那些监听器索引
7-5 实例

通过connection的连接事件来演示EventEmitter类的应用
创建main.js文件,代码如下:

var events = require('events');
var eventEmitter = new events.EventEmitter();

//监听器1
var listener1 = function listener1(){
   console.log('监听器 listener1 执行。');
}

//监听器2
var listener2 = function listener2(){
   console.log('监听器 listener2 执行。');
}

// 绑定 connection 事件,处理函数为 listener1 
eventEmitter.addListener('connection',listener1);

// 绑定 connection 事件,处理函数为 listener2
eventEmitter.on('connection',listener2);

var eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + '个监听器监听连接事件')

//触发 connection事件
eventEmitter.emit('connection')

// 移除监绑定的 listener1 函数
eventEmitter.removeListener('connection',listener1);
console.log("listener1 不再受监听。");


//触发 connection事件
eventEmitter.emit('connection')

eventListeners = eventEmitter.listenerCount('connection');
console.log(eventListeners + '个监听器监听连接事件')

console.log("程序执行完毕。");

7-6 error事件

EventEmitter定义了一个特殊的事件error,它包含了错误的语义,我们在遇到异常的时候,通常会触发error事件
当error被触发时,EventEmitter规定如果没有响应的监听器,Node.js会把他当做异常,退出程序并输出错误信息。
我们一般要为会触发error事件的对象设置监听器,别面遇到错误后整个程序崩溃,例如:

//error.js
var events = require('events');
var emitter = new events.EventEmitter();
emitter.emit('error');
error
7-7 继承EventEmitter

大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
为什么要这样做呢?原因有两点:
首先,具有某个实体功能的对象实现事件符合语义, 事件的监听和发生应该是一个对象的方法。
其次 JavaScript 的对象机制是基于原型的,支持 部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • Node.js EventEmitter Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队...
    FTOLsXD阅读 315评论 1 2
  • EventEmitter 类 events 模块只提供了一个对象: events.EventEmitter。Eve...
    ElfACCC阅读 303评论 0 0
  • Node.js EventEmitter Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队...
    yyshang阅读 246评论 0 1
  • https://nodejs.org/api/documentation.html 工具模块 Assert 测试 ...
    KeKeMars阅读 6,322评论 0 6
  • Node.js 回调函数 Node.js 异步编程的直接体现就是回调。 异步编程依托于回调来实现,但不能说使用了回...
    majun00阅读 444评论 0 0