本文转载请联系编程杂技公众号。events模块是Node.js中一个比较简单但是非常核心的模块。在Node.js中,许多模块继承自events模块。事件模块是发布订阅模式的实现。我们先来看看如何使用events模块。const{EventEmitter}=require('events');classEventsextendsEventEmitter{}constevents=newEvents();events.on('demo',()=>{console.log('emitdemoevent');});events.emit('演示');接下来我们看一下events模块的具体实现。1初始化当新建一个EventEmitter或其子类时,会进入EventEmitter的逻辑。functionEventEmitter(opts){EventEmitter.init.call(this,opts);}EventEmitter.init=function(opts){//如果未初始化或者没有自定义_events,则初始化if(this._events===undefined||this._events===ObjectGetPrototypeOf(this)._events){this._events=ObjectCreate(null);this._eventsCount=0;}/*初始化一类事件的处理函数数量阈值。我们可以通过setMaxListeners接口设置,如果没有显示设置,阈值为defaultMaxListeners(10)的值,可以通过getMaxListeners接口获取*/this._maxListeners=this._maxListeners||undefined;//是否为启用捕获promisereject,默认为falseEventEmitter的初始化主要是初始化一些数据结构和属性。唯一受支持的参数是captureRejections。captureRejections表示事件触发并执行处理程序时,EventEmitter是否捕获处理程序中的异常。我们稍后会详细解释。2订阅事件EventEmitter初始化完成后,我们就可以开始使用订阅和发布的功能了。我们可以通过addListener、prependListener、on、once来订阅事件。addListener和on是等价的。prependListener的区别是处理函数会插入到队头,默认追加到队尾。注册一次的处理函数最多执行一次。这四个API是通过_addListener函数实现的。我们来看看具体的实现。function_addListener(target,type,listener,prepend){letm;letevents;letexisting;events=target._events;//如果没有初始化就初始化_events,_eventsCount是事件类型的个数if(events===undefined){events=target.注册其他事件会触发newListener,相当于hook*/if(events.newListener!==undefined){target.emit('newListener',type,listener.listener?listener.listener:listener);//newListener处理function可能会修改_events,这里重新赋值events=target._events;}//判断是否已经有处理函数existing=events[type];}//如果不存在,则以一个函数,否则会以数组的形式存储if(existing===undefined){events[type]=listener;//添加一个事件类型,addone到事件类型个数++target._eventsCount;}else{/*existing表示函数之前注册过一次事件,否则表示existing为Array,则直接插入对应位置*/if(typeofexisting==='function'){existing=events[type]=prepend?[listener,existing]:[existing,listener];}elseif(prepend){existing.unshift(listener);}else{existing.push(listener);}//处理告警,可能处理函数过多,因为之前的没有删除,导致内存泄漏m=_getMaxListeners(target);//事件处理函数达到阈值,没有提示一封警告信如果(m>0&&existing.length>m&&!existing.warned){existing.warned=true;constw=newError('errormessage...');w.name='MaxListenersExceededWarning';w.emitter=target;w。type=type;w.count=existing.length;process.emitWarning(w);}}returntarget;}接下来我们看一下once的实现。相对于其他API,once的实现相对复杂一些,因为我们需要控制处理函数最多执行一次,所以需要保证在事件触发时,执行自定义函数,注册的事件需要被删除ventEmitter.prototype.once=functiononce(type,listener){this.on(type,_onceWrap(this,type,listener));returnthis;;unctiononceWrapper(){//还未触发if(!this.fired){//移除this.target.removeListener(this.type,this.wrapFn);//触发this.fired=true;//执行if(arguments.length===0)returnthis.listener.call(this.target);returnthis.listener.apply(this.target,arguments);}}//支持onceapifunction_onceWrap(target,type,listener){//fired是否执行过处理函数,wrapFn包装listener的函数conststate={fired:false,wrapFn:undefined,target,type,listener};//生成一个包装监听器的函数constwrapped=onceWrapper.bind(state);/*原来的函数监听器也挂在包装函数里,在事件前使用被触发,用户主动删除,见removeListener*/wrapped.listener=listener;//保存wrappedfunction执行后删除,见onceWrapperstate.wrapFn=wrapped;returnwrapped;}once函数构造上下文(state)保存user处理函数和执行状态等信息,然后通过bind返回一个用上下文(状态)包裹的函数注册到事件系统。当事件被触发时,首先在被包装函数中移除被包装函数,然后执行用户的函数。包裹充当劫持。此外,还需要将用户传入的函数保存在wrapped上。当用户在事件触发前删除事件或释放函数时,在遍历此类事件的处理函数的过程中,可以通过wrapped.listener找到对应的item。删除。3触发事件分析完事件的订阅,我们来看事件的触发。EventEmitter.prototype.emit=functionemit(type,...args){//触发的事件是否为错误,错误事件需要特殊处理letdoError=(type==='error');constevents=this._events;//定义了一个处理函数(不一定是类型事件处理函数)if(events!==undefined){/*如果触发的事件是错误的,并且监听了kErrorMonitor事件,就会触发kErrorMonitor事件*/if(doError&&events[kErrorMonitor]!==undefined)this.emit(kErrorMonitor,...args);//触发错误事件但未定义处理函数doError=(doError&&events.error===undefined);}elseif(!doError)//未定义如果处理函数触发了非错误事件,则不需要处理,返回false;//如果没有'error'eventlistenerthrow。//什么触发了错误事件,但没有定义一个处理错误事件的函数,会报错if(doError){leter;if(args.length>0)er=args[0];//第一个入参是Error的实例if(erinstanceofError){try{constcapture={};/*将stack属性注入capture对象,stack的值为Execute当前Error.captureStackTrace语句的堆栈信息,但不包括emit部分*/Error。captureStackTrace(capture,EventEmitter.prototype.emit);ObjectDefineProperty(er,kEnhanceStackBeforeInspector,{value:enhanceStackTrace.bind(this,er,capture),configurable:true});}catch{}thrower;//未处理的'error'事件}letstringifiedEr;const{inspect}=require('internal/util/inspect');try{stringifiedEr=inspect(er);}catch{stringifiedEr=er;}consterr=newERR_UNHANDLED_ERROR(stringifiedEr);err.context=er;throwerr;//Unhandled'error'event}//获取类型事件对应的处理函数consthandler=events[type];//没有则不处理if(handler===undefined)returnfalse;//等于函数描述只有一个if(typeofhandler==='function'){//直接执行constresult=ReflectApply(handler,this,args);//非空判断是否是promise,是否为promise需要处理,见addCatchif(result!==undefined&&result!==null){addCatch(this,result,type,args);}}else{//多个handler函数,同上constlen=handler.length;constlisteners=arrayClone(handler,len);for(leti=0;i
