EventEmitter的完成

媒介

事宜在js中异常的罕见,不管是浏览器照样node,这类事宜宣布/定阅情势的运用都是很罕见的。至于宣布/定阅情势和观察者情势是不是是同一种设想情势说法都有,这里不做详细的议论。在之前的项目中也曾本身完成过一个事宜模块,中心照样一个EventEmitter。下文就要连系node中的event模块剖析一下,一个EventEmitter应当怎样完成,有什么注重点。
源码地点
https://github.com/nodejs/nod…

基本的构造和设想

起首第一步就是一个EventEmitter的类,然后考虑一下这个类的实例属性和实例要领。
实例属性的话最基本的就是一个eventMap,可所以一个空对象,固然也能够如许建立Object.create(null)。假如须要还能够增添maxListener之类的属性。
实例要领的话,最中心的就是add delete emit分别是增加事宜,删除事宜,宣布事宜。固然现实完成的时刻,比方count,has,once(一次性增加),preAdd(增加在事宜队列最前面),这些要领则是能够依据现实需求去增加。

详细完成及注重点

以下代码均为简化的伪代码

add要领

EventEmitter.prototype.add = function(type, fn) {
    if (!isFunction(fn)) return;//推断是不是在监听中增加的是正当的函数
    //推断type是不是增加过,增加过一个照样多个函数
    if (this.event[type]) {
        if (isArray(this.event[type])){
            //假如想要完成preadd将push改成unshift即可
            this.event[type].push(fn);
        } else {
            //假如想要完成preadd转变递次
            this.event[type] = [this.event[type], fn];
        }
    } else {
        this.event[type] = fn;
    }
}

once要领

参考一下node的once要领

function onceWrapper(...args) {
  if (!this.fired) {
    this.target.removeListener(this.type, this.wrapFn);
    this.fired = true;
    Reflect.apply(this.listener, this.target, args);
  }
}

function _onceWrap(target, type, listener) {
  var state = { fired: false, wrapFn: undefined, target, type, listener };
  var wrapped = onceWrapper.bind(state);
  wrapped.listener = listener;
  state.wrapFn = wrapped;
  return wrapped;
}

EventEmitter.prototype.once = function once(type, listener) {
  this.on(type, _onceWrap(this, type, listener));
  return this;
};

函数用onceWrap包裹,运行前须要对增加的监听举行移除

delete

很简单理清晰几种边境状况就能够了

EventEmitter.prototype.delete = function(type, fn) {
    //直接删除整类监听
    if(fn === undefined){
        this.events[type] && delete this.events[type];
    }else{
        //推断fn正当性就省了
        if(this.events[type]) {
            if (this.events[type] === fn) {
                delete this.events[type];
            } else {
                for (var i in this.events[type]) {
                    if(this.events[type][i] === fn){
                        if (i === 0) {
                            this.events[type].shift();
                        } else {
                            this.events[type].splice(i,1);
                        }
                    }
                }
                if(this.events[type].length === 1) this.events[type] = this.events[type][0];
            }
        }
    }
    
}

emit

EventEmitter.prototype.emit = function(type) {
    //猎取参数
    var args = [].slice.call(arguments, 1);
    var handler = events[type];

    if (handler === undefined) return false;

    if (typeof handler === 'function') {
        handle.apply(this, args);
    } else {
        var len = handler.length;
        const listeners = arrayClone(handler, len);
        for (var i = 0; i < len; ++i)
             handle[i].apply(this, args);
   }
}

宣布事宜有两个注重点,一个是注重参数的保存,另一个则是上下文,这里上下文是直接取了event实例的上下文,也能够考虑一下手动传入上下文的情势,或者说fn在定义的时刻直接写成箭头函数,也能够防备上下文成为eventEmit实例的上下文。

毛病处理

这里提一下node的event的毛病事宜

当 EventEmitter 实例中发作毛病时,会触发一个 ‘error’ 事宜。 这在 Node.js 中是特殊状况。

假如 EventEmitter 没有为 ‘error’ 事宜注册最少一个监听器,则当 ‘error’ 事宜触发时,会抛出毛病、打印客栈跟踪、且退出 Node.js 历程。

为了防备 Node.js 历程崩溃,能够在 process 对象的 uncaughtException 事宜上注册监听器,或运用 domain 模块。 (注重,domain 模块已被烧毁。)

作为最好实践,应当一直为 ‘error’ 事宜注册监听器。

假如有须要在本身的实践中也能够增添一个毛病处理的机制,保证event实例的稳定性

总结

一个事宜定阅宣布类实在不难完成,而在node中有许多凶猛的类都是继续的事宜类,而以后我会接着对node文件体系举行进修

    原文作者:求实亭下
    原文地址: https://segmentfault.com/a/1190000014206309
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞