Nodejs EventEmitter 解读
目录
- events module
- Class EventEmitter
- TODO
events
module
先说一下网上似乎很多人提供的使用例子来看,都是如下的
1 | var EventEmitter = require('events').EventEmitter; |
然而在源码当中有这样的一句话
1 | module.exports = EventEmitter; |
所以require('events').EventEmitter
和require('events')
是一样的。所以以后大家可以直接写
1 | var EventEmitter = require('events'); |
Class EventEmitter
在源码中,构造函数极其的简单。
1 | function EventEmitter() { |
思路很简单,就是直接调用EventEmitter
上的静态方法init
进行构造。之后我们会介绍EventEmitter.init
方法
EventEmitter Static Method And Property
挂在到EventEmitter上的静态属性和方法还是很多的,先说明一下静态属性:
defaultMaxListeners
usingDomains
defaultMaxListeners
顾名思义,默认的最大callback数量,就是如果当前对象没有指定_maxListeners
,默认使用的就是这个值,默认是10
更多请看_maxListeners
usingDomains
待定
init()
1 | EventEmitter.init = function() { |
先不管domain
恨死干什么用的,总之init
函数给this
挂上了以下四个属性:
domain
listenerCount(emitter, type)
获取emitter
中指定类型的callback数量
这里有一个比较特殊的地方,就是他对对象是否含有listenerCount
方法进行了判断
1 | EventEmitter.listenerCount = function(emitter, type) { |
疑问:为什么要判断原型上是否含有listenerCount
方法呢?
EventEmitter Property
_events
这个属性是一个事件的缓存列表,他的key
就是事件名称,value
就是事件的回调函数,有如下几种取值:
Function
Array
当只有一个回调函数的时候,就直接存储为回调函数本身,否则包装成数组进行存储
_eventsCount
顾名思义,就是事件的个数,这里有几个需要注意的就是,这个之有且仅在添加一个没有过的事件的时候才会加一,如果你给一个事件添加了多了callback的话,这个值并不会加一的。
_maxListeners
这个就是可以表示一个event的最大callback的个数。如果小于等于这个个数,不会产生任何问题。但是如果大于这个限制,将会弹出警告,说可能会导致内存泄露。
我猜测可能是因为过多的callback会保存过多的context,从而导致内存泄露。
敬请更正
EventEmitter Method
先从最常用的讲起,就是添加事件侦听喽
addListener() = on()
首先大家一定很熟悉on
方法,大家一定想不到的是,竟然还有一个叫addListener
方法。那么这两个方法有什么区别呢?
答案就是什么区别都木有~
而且在定义的时候,定义的是addListener
而不是on
,从源码中便可以看出来
1 | EventEmitter.prototype.addListener = function addListener(type, listener) { |
所以on
更多的是一种简化,而正统的却是addListener
,但是虽然这个正统,相信也没有几个人来使用吧,毕竟实在是太那啥了,是吧-_-||
好的下面言归正传,整个添加函数的流程可以看做是这样的(大家可以对照的源码看,我就不在这里把源码全部都粘贴过来了,并且我这里可能为了语句的通顺改变部分代码的执行顺序,大家请自行看出来。。。):
接受两个参数,分别是
type
表示事件,就是event,第二个是listener
,侦听器,也就是callback如果
listener
不是函数,很明显啊,抛出错误不解释检测当前对象上是否存在
_events
属性,如果不存在创建之,并顺手初始化_eventsCount
为0.另
events
为this._events
如果
events
中没有type
的listener
,那么不解释,添加之~即events[type] = listener
如果
events
里面有的话,不解释,一个就数组包装一下,两个直接push如果
type
类型的listener
超过两个了,那么就检测一下有没有超过长度限制,具体的检测逻辑我就不在这里详细的说明了,总之就是给将检测结果挂到events[type]
的warned
属性上了。如果有对应的
newListener
事件侦听的话,就直接用type
和listener.listener?listener.listener:listener
触发之返回
this
至此函数执行完毕
那么下一个就讲一下如果触发事件吧
newListener
事件是在事件真正添加之前触发的
emit()
很早以前我认为emit
只能emit
一个参数,到现在我猜明白,想几个就几个,没人限制你。
emit
有一个很好玩的特性就是,如果你emit
一个"error"
,如果没有事件侦听这个error的话,就会直接throw出一个error来,而其他的事件不会又这种效果,这就意味着我们最好要侦听error
事件,万一出了一点错误,那将是崩溃的节奏啊~
检测
type
是否为"error"
如果是,并且没有监听到
error
,throw出pass进来的错误或者构建一个未捕捉的错误弹出。如果没有对应的回调函数的话,返回
false
获取构造函数并赋值给
handler
,这个值有可能是函数,或者是数组。根据参数个数调用对应的函数,顺序依次运行
handler
返回
true
handler
的执行
在handler
执行的时候,会先将当前队列复制一份,然后再进行执行。并且根据参数个数用call或者apply执行函数。放置在某一次执行期间突然掺入了其他的callback或者删除了callback,从而引发错误。
我原本以为会使用process.nextTick
进行异步的执行,后来一想不对啊,肯定要按照添加的顺序进行执行,所以依次调用。
根据参数个数进行加速
见
1 | // 没有参数的调用 |
源码对不同的情况进行了加速,因为人们大部分情况都是使用0参数或者1参数的,所以对这几种情况进行处理是非常高效的。
once()
这个最简单了,就是用一个函数封装一下传入的callback,然后将这个函数,addListener
进入到事件中。关于这个封装函数的写法,如下
1 | var fired = false; |
具体的就不多说了,总之就是执行后,先删除这个包装过的callback。
我的疑问:这里我有一点不太理解的地方就是为什么还需要
fired
进行标记一下呢?
removeListener(type, listener)
删除指定类型的指定callback的事件 很简单,分为一下几步
如果
listener
不是函数,抛出错误如果
events
不存在或者对应的事件不存在,返回this
另
list
为events[type]
如果
list
就是那个callback或者list.listener
是那个callback,删除否则如果这是一个数组,那么找到对应的
listener
删除,否则返回this
如果上面有任意一个删除之后
_eventsCount
为0了,直接重新赋值this._events = {}
(其实并不知道这个意义何在)如果有任何一个删除,并且有事件侦听到了
removeListener
,触发之,传递type
和listener
返回
this
在事件删除之后才调用
removeListener
事件
removeAllListeners(type)
根据传递的参数来决定是删除全部的还是只删除指定事件的全部
确保存在
this._events
,否则返回this
如果没有侦听
removeListener
如果没有传递
type
,重新赋值this._events
和this._eventsCount
如果传递了
type
,删除type
对应的事件,根据情况重新赋值~~~
如果侦听了
removeListener
,除了这个本身,依次调用removeAllLiseners()
进行删除,最后删除removeListener
这个事件对于每一个callback,依次调用
removeListener
方法进行删除返回
this
listeners(type)
获取指定类型的callback,否则为[]
listenerCount(type)
获取指定类型的callback的数量