如果大家研究过node_club源码,想必不会对EventProxy模块感到陌生吧!该模块是由@朴灵所创作出来的,该模块有如下几大特点:
- 利用事件机制解耦复杂业务逻辑
- 移除被广为诟病的深度callback嵌套问题
- 将串行等待变成并行等待,提升多异步场景下的执行效率
- 无平台依赖,适合前后端,能用于浏览器和Node.js
想要获取eventproxy源码的童鞋们可以通过命令npm install eventproxy或者直接去github网站获取,网址为:[enter link description here][1]
[1]: https://github.com/JacksonTian/eventproxy 那里面有较为详细的介绍,好了,接下来我们步入正题,进行对eventproxy模块源码的探究之旅吧!
由于eventproxy模块中的其他方法都相对来说比较简单,这里,我就不再多费笔墨来解读呢,主要针对其中的_assign方法进行分析,它也是eventproxy的核心所在吧!
var _assign = function (eventname1, eventname2, cb, once){...}
很多内置的函数都使用到了该函数,比如:EventProxy.prototype.all和EventProxy.prototype.tail等方法。好了,接下来我们将其源码剪切下来,其中有对关键代码的详细注释,相信大家一看就很清楚呢!
var _assign = function (eventname1, eventname2, cb, once) {
var proxy = this, length, index = 0, argsLength = arguments.length,
bind, _all,
callback, events, isOnce, times = 0, flag = {};
// Check the arguments length.
if (argsLength < 3) {
//由于参数列表的最后两项分别是回调函数
//和是否只进行一次监听的标识位,因此加上要监听一事件,
//所以参数列表的长度至少是3位或者3位以上,否则直接返回。
return this;
}
//获取所有要进行监听的事件名
events = Array.prototype.slice.apply(arguments, [0, argsLength - 2]);
//获取回调函数
callback = arguments[argsLength - 2];
//获取监听标识位
isOnce = arguments[argsLength - 1];
// Check the callback type.
if (typeof callback !== "function") {
return this;
}
length = events.length;
//bind函数是关键,主要用于各个需要监听的事件绑定到一个指定函数里,主要通过
//method指定的方法进行事件的绑定(once或者bind函数),同时在function(data){...}
//函数里对将传递过来的参数记录下来,以便之后传递给回调函数,并且在这个函数里地相应事件的触发
//进行计数,以便在所有监听的事件都触发后,对callback函数进行回调,见下文
bind = function (key) {
var method = isOnce ? "once" : "bind";
//proxy[method]事实就是指eventproxy模块上下文的bind和once事件,进行相应的绑定操作
proxy[method](key, function (data) {
proxy._fired[key] = proxy._fired[key] || {};
//对相应事件传递过来的实参进行记录
proxy._fired[key].data = data;
if (!flag[key]) {
flag[key] = true;
//times用于对触发相应事件时完成对其的计数功能
times++;
}
});
};
for (index = 0; index < length; index++) {
//依次对监听的事件进行绑定,使用上面的bind函数(注意不是上下文的bind函数)
bind(events[index]);
}
_all = function () {
if (times < length) {
//这里是重点,作用是为了判断是否所有的监听事件都已经完成了触发动作(可以通过emit事件)
//,如果还有未触发的事件,则跳出当前函数,也就放弃了对回调函数的调用过程,理解这点对
//assign函数的原理也就差不多理清楚了
return;
}
var data = [];
for (index = 0; index < length; index++) {
//获取所有监听事件所传递过来的实参,以便给回调函数使用
data.push(proxy._fired[events[index]].data);
}
if (isOnce) {
//当isOnce为true时,则将all事件进行去除绑定,那么回调函数也只会在
//所有监听事件触发完成后被调用一次,此后便不会再进行回调。
//相反,如果当isOnce为false时,则在所有监听事件触发完成之后的时间里,
//只要触发任意的监听函数都会对回调函数进行回调调用。这也就assign和assignAll的本质差异!!!
proxy.unbind("all", _all);
}
//对回调函数进行回调(需要理解javascript的apply或者cal方法的使用)
callback.apply(null, data);
};
//对_all事件进行绑定,以便能够正确对回调函数进行回调
proxy.bind("all", _all);
};
以上便是对_assign方法的详细说明,注释已经写得比较清楚呢。如果对上述方法的原理理解透彻呢,想必会对看懂node_club源码有所帮助的。顺便提醒一个,上述函数中涉及到了javascript的函数闭包的内容,比如为什么_assign函数中的局部变量times能够一起存活下去?这里大家就需要对函数闭包的知识有所了解才能弄懂了,在这里只是顺带提一下,应该说闭包的内容还是比较复杂的,三言两语也说不清,不懂的就直接去找相关资料进行学习吧。如果有机会,自己也会把自己对对闭包的理解写出来,供大家评阅!!!上述有什么表述不清或者不准确的地方欢迎大家的随时拍砖!!!共同学习!!!
貌似相对 step没啥特别的优势呢?
step?不太了解您所谓的step概念,是否可以说得具体些?至于eventproxy本身就是一个轻量级的异步/基于事件的的模块,至于本身所具有的特点已经在文章中有所提及呢。当然自己也完全可以参考本思路,自己开发出一个基于异步/基于事件的模块出来!
谢谢分享,看完eventproxy源码之后,再看nodeclue就简单易懂了。
虽然说eventproxy可以用在前端,不过好像也不太适合前端用。
共同进步!!!
今天才看到这个帖子。太感谢楼主了。
当初看step的时候,还不支持并行。
可能前端的应用场景不太相同。但是如果用模块和事件较多的话,它就会体现出价值的。
要用Jscex哇!
楼上是老赵
我想问一下关于代码最前面的这段是什么意思,能详细说说么……
;
(function (name, definition) {
// this is considered "safe":
var hasDefine = typeof define === 'function',
// hasDefine = typeof define === 'function',
hasExports = typeof module !== 'undefined' && module.exports;
if (hasDefine) {
// AMD Module or CMD Module
define(definition);
} else if (hasExports) {
// Node.js Module
module.exports = definition();
} else {
// Assign to common namespaces or simply the global object (window)
this[name] = definition();
}
})
@ggaaooppeenngg umd
比较想问下这个模块相对 async 有什么优势?async 还不需要自己去关注事件点,用起来更加省心~
@gvforjob after方法很实用, 各有所长吧, 我一般都是混着用的
之前我用的也是这个模块,但对个人来说有些不适,后来自己就开发了一套,部分内容比起 eventproxy 要好很多。
eventproxy
需要先创建,然后再 emit 触发,不是很灵活。从上面的例子可以看出来。
howdo
而 howdo 却不一样。 howdo.task().task().task() 链式创建有顺序的任务。 这些任务怎么执行?有2个 API ,一个是串行(follow),一个是并行(together)。 项目地址:https://www.npmjs.org/package/howdo 同时,howdo 也支持浏览器端的异步流程控制。