nodeJS之eventproxy源码解读

1.源码缩影
!(function (name, definition) {
  var hasDefine = typeof define === 'function',//检查上下文环境是否为AMD或CMD
    hasExports =typeof module !== 'undefined' && module.exports;//检查上下文环境是否为Node

  if (hasDefine) {
   define(definition); //AMD环境或CMD环境
  } else if (hasExports) {
   module.exports = definition(require('debug')('eventproxy'));//构建为普通Node模块
  } else {
    this[name] =definition(); //构建在window.name中
  }
})('EventProxy', function (debug) {
  debug = debug || function () {}; //为debug设置默认值
 
  var EventProxy = function () { //EventProxy构造函数
    if (!(thisinstanceof EventProxy)) { //防止构造函数以call或apply方式被其它对象调用,只能new
     return new EventProxy();
    }
   this._callbacks = {}; //new之后对象属性_callbacks,保存要处理的回调函数
    this._fired= {}; //new之后对象属性_fired,保存回调之后的结果
  };
  EventProxy.prototype... //EventProxy的诸原型函数,所谓原型函数,就是这些函数为new之后对象多共享
  EventProxy.create = function () {...};//EventProxy的属性create,指向普通函数
  EventProxy.EventProxy = EventProxy;//EventProxy的属性EventProxy指向自身

  return EventProxy; //返回EventProxy构造函数
});


源码解读:
上面大致展示了eventproxy的结构,首先它使用闭包的形式保证了代码的整洁;其次为了适应不同的使用场合,代码中都做了必要的处理,而我们使用最多的无疑是作为nodejs的模块:检查exports,如果存在,则构建为nodejs模块:module.exports=definition(require('debug')('eventproxy'));而definition则是function(debug)开始一直到文件结构的函数,其中debug作为调试,暂且不管,剩下的就是EventProxy函构造函数,EventProxy原型函数,EventProxy.create一般函数,最后返回EventProxy函构造函数.于是构建的结果相当于module.exports=EventProxy;所以在其它模块中的require("eventproxy")指向的就是EventProxy构造函数.


2.原型函数addListener,别名bind,on,subscribe
EventProxy.prototype.addListener = function (ev, callback) {
    debug('Addlistener for %s', ev);
   this._callbacks[ev] = this._callbacks[ev] || [];
   this._callbacks[ev].push(callback);
    returnthis;
};

源码解读:
该方法接收两个参数:事件名称和回调函数,首先在
_callbacks对象上面开辟事件的数组,之后把回调函数放进该数组中.

3.原型函数headbind
EventProxy.prototype.headbind = function (ev,callback) {
    debug('Addlistener for %s', ev);
   this._callbacks[ev] = this._callbacks[ev] || [];
   this._callbacks[ev].unshift(callback);
    returnthis;
};

源码解读:
该方法与addListene方法相同,只不过是把回调函数放在了数组的第一个

4.原型函数removeListener,别名unbind
EventProxy.prototype.removeListener =function (eventName, callback) {
    var calls =this._callbacks;
    if(!eventName) {
     debug('Remove all listeners');
     this._callbacks = {};
    } else{
     if (!callback) {
       debug('Remove all listeners of %s', eventName);
       calls[eventName] = [];
     } else {
       var list = calls[eventName];
       if (list) {
         var l = list.length;
         for (var i = 0; i < l; i++) {
           if (callback === list[i]) {
             debug('Remove a listener of %s', eventName);
             list[i] = null;
           }
         }
       }
     }
    }
    returnthis;
};

源码解读:
该方法是为移除特定的回调函数:(1)两个参数都不存在,移除所有回调函数(2)eventName存在,callback不存在,移除eventName上的所有函数(3)eventName存在,callback存在,移除eventName上的callback函数.

4.原型函数removeAllListeners
EventProxy.prototype.removeAllListeners =function (event) {
    returnthis.unbind(event);
};

源码解读:
该方法是为移除event上所有的函数.

5.原型函数bindForAll
EventProxy.prototype.bindForAll = function(callback) {
   this.bind(ALL_EVENT, callback);
};

源码解读:
该方法是为绑定全局回调函数__all__为callback

6.原型函数unbindForAll
EventProxy.prototype.unbindForAll = function (callback) {
   this.unbind(ALL_EVENT, callback);
};

源码解读:
该方法是为移除全局回调函数__all__的callback函数

7.原型函数trigger,别名emit,fire
EventProxy.prototype.trigger = function(eventName, data) {
    var list,ev, callback, args, i, l;
    var both =2;
    var calls =this._callbacks;
    debug('Emitevent %s with data %j', eventName, data);
    while(both--) { //执行两次,第一次执行eventName的回调函数,第二次执行全局__all__的回调函数
     ev = both ? eventName : ALL_EVENT;
     list = calls[ev]; //回调函数列表
     if (list) {
       for (i = 0, l = list.length; i < l; i++) {
         if (!(callback = list[i])) { //如果回调函数不存在,移除
           list.splice(i, 1);
           i--;
           l--;
         } else {
           args = both ? SLICE.call(arguments, 1) : arguments;
           callback.apply(this, args); //非全局回调函数,值传入data,全局传入全部参数
         }
       }
     }
    }
    returnthis;
};

源码解读:
该方法是为eventName事件触发函数,或对eventName事件注值函数:(1)事件触发会做两件事情,一是触发该事件的函数数组,把data出入一一执行,是执行全局回调函数数组,把事件名称和data传进去,它内部会判断是否所有要处理的事件都已处理完毕,处理完毕就执行后面真正的全局回调,如果没有完成则会退出全局回调函数.

8.原型函数once
EventProxy.prototype.once = function (ev,callback) {
    var self =this;
    var wrapper= function () {
     callback.apply(self, arguments); //回调wrapper时回调源回调函数,再解绑
     self.unbind(ev, wrapper);
    };
   this.bind(ev, wrapper); //绑定的是wrapper,会回调wrapper
    returnthis;
};

源码解读:
该方法是为一次性绑定函数,即回调之后会移除,是最常用的绑定函数,它绑定的不是原有回调函数,而是进行了包装,因为在包装内部要做解绑处理;虽然进行了包装,但是回调的时候传入的参数还是在内部传入远回调函数执行,只是多了一步解绑.

9.临时变量later
var later = typeof process !== 'undefined'&& process.nextTick || function (fn) {
   setTimeout(fn, 0);
};

源码解读:
存在process.nextTick,later就等于process.nextTick,没有就用setTimeout.

10.原型函数emitLater
EventProxy.prototype.emitLater = function () {
    var self =this;
    var args =arguments;
   later(function () {
     self.trigger.apply(self, args);
    });
};

源码解读:
迟缓触发,参数应为eventName,data.

11.原型函数immediate,别名asap
EventProxy.prototype.immediate = function (ev, callback,data) {
   this.bind(ev, callback);
   this.trigger(ev, data);
    returnthis;
};

源码解读:
即时的,绑定事件回调函数,触发


12.临时变量_assign
var _assign = function (eventname1, eventname2, cb, once) {
    var proxy =this;
    varargsLength = arguments.length; //参数个数
   var times = 0; //已触发注值个数
    var flag ={}; //已触发注值标志

    // Check thearguments length.
    if(argsLength < 3) { //参数小于3,返回
     return this;
    }

    var events =SLICE.call(arguments, 0, -2); //取得传入的event数组
    var callback= arguments[argsLength - 2]; //取得回调函数
    var isOnce =arguments[argsLength - 1]; //取得是否一次调用标志

    // Check thecallback type.
    if (typeofcallback !== "function") { //没有回调函数,返回
     return this;
    }
   debug('Assign listener for events %j, once is %s', events,!!isOnce);
    var bind =function (key) { //为处理回调函数
     var method = isOnce ? "once" : "bind"; //根据标志取得调用绑到的方法
     proxy[method](key, function (data) { //绑定事件和回调函数,回调函数为内建,回调的时候会传入data参数
       proxy._fired[key] = proxy._fired[key] || {}; //初始化结果保存位置
       proxy._fired[key].data = data; //把数据注入_fired中
       if (!flag[key]) { //没有回调过,标志置为true,事件回调个数+1
         flag[key] = true;
         times++;
       }
     });
    };

    var length =events.length;
    for (varindex = 0; index < length; index++) { //为每一个事件绑定回调函数
     bind(events[index]);
    }

    var _all =function (event) { //全局回调函数
     if (times < length) { //判断是否所有事件都已回调注值
       return;
     }
     if (!flag[event]) { //判断传入的事件回调标志,无必要
       return;
     }
     var data = [];
     for (var index = 0; index < length; index++) {
       data.push(proxy._fired[events[index]].data); //把所有注入的值放入到data中
     }
     if (isOnce) { //如果是一次绑定,解绑全局函数
       proxy.unbindForAll(_all);
     }
     debug('Events %j all emited with data %j', events, data);
     callback.apply(null, data); //回调用户传入的回调函数,把data出入,完成
    };
   proxy.bindForAll(_all); //绑定全局回调函数
};

源码解读:
及其重要,真正的assign函数,其会根据用户简单的事件数组,是否一次绑定标志,回调函数,为每一个事件绑定特定的回调函数,当执行这些回调函数的时候,会把触发的值注入,标志此事件回调完成,检查是否都已回调完成,如果是就调用全局回调函数,即把诸多事件注入的值整理出入用户传入的回调函数.


13.原型函数all,别名assign
EventProxy.prototype.all = function (eventname1, eventname2,callback) {
    var args =CONCAT.apply([], arguments);
   args.push(true);
   _assign.apply(this, args);
    returnthis;
};

源码解读:
一次绑定的_assign函数


14.原型函数fail
EventProxy.prototype.fail = function (callback) {
    var that =this;
   that.once('error', function (err) {
     that.unbind();
     callback.apply(null, arguments);
    });
    returnthis;
};

源码解读:
一次绑定error事件,触发的时候会解绑所有回调函数,把错误传入callback执行


15.原型函数tail,别名assignAll,assignAlways
EventProxy.prototype.tail = function () {
    var args =CONCAT.apply([], arguments);
   args.push(false);
   _assign.apply(this, args);
    returnthis;
};

源码解读:
永久绑定的_assign函数


16.原型函数after
EventProxy.prototype.after = function (eventName, times, callback){
    if (times=== 0) { //容错吧,不太好
     callback.call(null, []);
     return this;
    }
    var proxy =this,
     firedData = []; //用户自己注值的地方
   this._after = this._after || {};
    var group =eventName + '_group';
   this._after[group] = { //注值的地方
     index: 0,
     results: []
    };
    debug('Afteremit %s times, event %s\'s listenner will execute', times,eventName);
    var all =function (name, data) {
     if (name === eventName) { //内部未实现,可能是让用户自己实现吧,逻辑简单?
       times--;
       firedData.push(data);
       if (times < 1) {
         debug('Event %s was emit %s, and execute the listenner', eventName,times);
         proxy.unbindForAll(all);
         callback.apply(null, [firedData]);
       }
     }
     if (name === group) { //与原型函数group配合使用
       times--; //注值一次,个数-1,感觉放在此处不妥
       proxy._after[group].results[data.index] = data.result; //注值
       if (times < 1) { //如果times=0,全部注值完成,解绑全局函数,把值传入callback执行
         debug('Event %s was emit %s, and execute the listenner', eventName,times);
         proxy.unbindForAll(all);
         callback.call(null, proxy._after[group].results);
       }
     }
    };
   proxy.bindForAll(all); //绑定全局函数
   return this;
};

源码解读:
此函数是为相同的事件的处理,好比多个文件的读取,次数就是文件的个数;内部没有为该事件绑定特殊的回调函数,而是永久性的绑定了一个全局函数,所以在每一次该事件的注入,即回调,都是执行的全局函数,二全局函数会执行一次,就注值一次,个数-1,直到个数为0就解绑全局,把所有注入的值出入回调函数执行.


17.原型函数group
EventProxy.prototype.group = function (eventName, callback) {
    var that =this;
    var group =eventName + '_group';
    var index =that._after[group].index; //取出index,一个变量
   that._after[group].index++;
    returnfunction (err, data) { //返回的是nodejs的回调函数,只不过帮用户处理了
     if (err) {
       // put all arguments to the error handler
       return that.emit.apply(that,['error'].concat(SLICE.call(arguments)));
     }
     that.emit(group, { //注值
       index: index, //当前下标
       //如果有callback,会返回经过callback处理的data,没有直接返回data
       result: callback ? callback.apply(null, SLICE.call(arguments, 1)) :data
     });
    };
};

源码解读:
此函数为协助after的执行,源码说可以实现事件注入的有序,单我个人认为没有实现,它是类似这样的实现:
var ep = new EventProxy();
ep.after('file', files.length, function (list) {
   // Ordered results
});
for (var i = 0; i < files.length; i++) {
   fs.readFile(files[i], 'utf-8',ep.group('file'));
}
可是在下面文件注值的循环中也是异步处理的,实际注值的顺序是这些个文件读取完成的顺序,是不确定的;不确定的东西可以说是有序的么,有序是否应该是按照原文件列表的顺序呢,这种方式和name=== eventName的处理是没有区别的.


18.原型函数any
EventProxy.prototype.any = function () {
    var proxy =this,
     callback = arguments[arguments.length - 1],
     events = SLICE.call(arguments, 0, -1),
     _eventName = events.join("_");

    debug('Addlistenner for Any of events %j emit', events);
   proxy.once(_eventName, callback);

    var _bind =function (key) {
     proxy.bind(key, function (data) {
       debug('One of events %j emited, execute the listenner');
       proxy.trigger(_eventName, {"data": data, eventName: key});
     });
    };

    for (varindex = 0; index < events.length; index++) {
     _bind(events[index]);
    }
};

源码解读:
参数如:eventName1,eventName1,callback.它先一次绑定全局函数为callback,也为每一个事件绑定函数,内部会触发全局函数,进而实现任意一个事件触发,就回调.

19.原型函数not
EventProxy.prototype.not = function (eventName, callback) {
    var proxy =this;
    debug('Addlistenner for not event %s', eventName);
   proxy.bindForAll(function (name, data) {
     if (name !== eventName) {
       debug('listenner execute of event %s emit, but not event %s.',name, eventName);
       callback(data);
     }
    });
};

源码解读:
内部绑定全局函数,如果触发的事件不是
eventName,就执行回调.

20.原型函数done
EventProxy.prototype.done =function (handler, callback) {
    var that =this;
    returnfunction (err, data) {
     if (err) {
       return that.emit.apply(that,['error'].concat(SLICE.call(arguments)));
     }
     var args = SLICE.call(arguments, 1);

     if (typeof handler === 'string') {
       if (callback) {
         return that.emit(handler, callback.apply(null, args));
       } else {
         return that.emit.apply(that, [handler].concat(args));
       }
     }
     if (arguments.length <= 2) {
       return handler(data);
     }

     handler.apply(null, args);
    };
};

源码解读:
返回nodejs回调函数,帮用户处理一些错误信息而已:(1)handler是字符串,如果callback存在,则把callback处理的data触发handler事件,如果callback不存在,则直接data触发handler事件;(2)handler是函数,handler(data);(3)handler(arguments)


21.原型函数doneLater
EventProxy.prototype.doneLater = function (handler, callback){
    var_doneHandler = this.done(handler, callback);
    returnfunction (err, data) {
     var args = arguments;
     later(function () {
       _doneHandler.apply(null, args);
     });
    };
};

源码解读:
later + done

22.构造函数属性create
EventProxy.create =function () {
    var ep = newEventProxy();
    var args =CONCAT.apply([], arguments);
    if(args.length) {
     var errorHandler = args[args.length - 1];
     var callback = args[args.length - 2];
     if (typeof errorHandler === 'function' && typeof callback=== 'function') {
       args.pop();
       ep.fail(errorHandler);
     }
     ep.assign.apply(ep, args);
    }
    returnep;
};

源码解读:
assign的快捷方式,可以:EventProxy.create(eventName1,eventName2,callback,errorHandler),或者EventProxy.create(eventName1,eventName2,callback).fail( errorHandler)或者把前面的事件放在数组中.

总结:
eventproxy主要是为了解决在nodejs中异步调用深度嵌套的问题,在一定程度上也提高了效率,因为深度嵌套的回调是会产生依赖的,违反了nodejs异步的初衷.
eventproxy的设计是基于这样一种场景,比如要在前端显示话题topic的信息,则在后台不但要拿到话题的信息,还要拿到其作者的信息,其所在栏目的信息,其回复的信息等,要拿到所有这些的信息,如果不采用深度嵌套调用的方式,是无法知道什么时候所有的异步都完成了.
基于这样一个场景,作者就想在一个地方存着这些回调的值,好比栏目信息取得了放进去,作者信息取了放进去,可是还是不知道什么时候都取过来了,于是需要检查,取进去一次值就检查一次.
eventproxy中_fired就是存值的地方,形如
_fired[eventName].data=data;_callbacks是存放回调函数的地方,回调函数是干什么用的,是存值用的.在用户说明了要一块执行的一组事件后, eventproxy会问每个事件创建回调函数,其接受data参数,形如 _callbacks[ eventName]= [callback1,callback2]; 当用户取到某个事件的值时,就触发该事件,传入值,就完成了值的注入,每次注入会检查是否全部注入,全部注入会把全部的值处理为数组放入全局回调函数中回调,全局回调函数即是用户传入的callback.如下图:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值