Nodejs EventProxy模块代码的研究和心得
Nodejs EventProxy模块代码的研究和心得
如果大家研究过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能够一起存活下去?这里大家就需要对函数闭包的知识有所了解才能弄懂了,在这里只是顺带提一下,应该说闭包的内容还是比较复杂的,三言两语也说不清,不懂的就直接去找相关资料进行学习吧。如果有机会,自己也会把自己对对闭包的理解写出来,供大家评阅!!!上述有什么表述不清或者不准确的地方欢迎大家的随时拍砖!!!共同学习!!!
Nodejs EventProxy模块代码的研究和心得
引言
如果你研究过 node_club
源码,相信你不会对 EventProxy
模块感到陌生。该模块是由 @朴灵 所创建,其主要特点包括:
- 利用事件机制解耦复杂业务逻辑
- 移除被广为诟病的深度 callback 嵌套问题
- 将串行等待变成并行等待,提升多异步场景下的执行效率
- 无平台依赖,适合前后端,能用于浏览器和 Node.js
你可以通过 npm install eventproxy
或者访问 GitHub 获取源码。
探究 EventProxy 模块源码
在这篇文章中,我将主要分析 EventProxy
的核心方法 _assign
。这个方法是整个模块的关键所在,涉及到事件绑定、回调处理等多个方面。
_assign
方法详解
以下是 _assign
方法的源码及注释:
var _assign = function (eventname1, eventname2, cb, once) {
var proxy = this, length, index = 0, argsLength = arguments.length,
bind, _all,
callback, events, isOnce, times = 0, flag = {};
// 检查参数数量
if (argsLength < 3) {
return this;
}
// 获取所有要监听的事件名
events = Array.prototype.slice.apply(arguments, [0, argsLength - 2]);
// 获取回调函数
callback = arguments[argsLength - 2];
// 获取监听标识位
isOnce = arguments[argsLength - 1];
// 检查回调函数类型
if (typeof callback !== "function") {
return this;
}
length = events.length;
// 绑定事件的函数
bind = function (key) {
var method = isOnce ? "once" : "bind";
proxy[method](key, function (data) {
proxy._fired[key] = proxy._fired[key] || {};
proxy._fired[key].data = data;
if (!flag[key]) {
flag[key] = true;
times++;
}
});
};
// 对每个事件进行绑定
for (index = 0; index < length; index++) {
bind(events[index]);
}
// 处理所有事件触发后的回调
_all = function () {
if (times < length) {
return;
}
var data = [];
for (index = 0; index < length; index++) {
data.push(proxy._fired[events[index]].data);
}
if (isOnce) {
proxy.unbind("all", _all);
}
callback.apply(null, data);
};
// 绑定 all 事件以处理所有事件触发后的回调
proxy.bind("all", _all);
};
代码解析
- 参数检查:首先检查传入的参数数量,确保至少包含三个参数:事件名、回调函数以及监听标识位。
- 事件绑定:遍历所有事件名,对每个事件使用
once
或bind
方法进行绑定。绑定的回调函数会记录事件数据并增加计数器times
。 - 回调处理:定义
_all
函数,在所有事件触发后调用。如果设置了isOnce
,则在第一次调用后解除绑定;否则,每次事件触发后都会调用回调函数。 - 事件监听:最后,绑定
all
事件以确保所有事件触发后的回调能够正确执行。
总结
通过深入分析 _assign
方法,我们可以更好地理解 EventProxy
模块如何通过事件机制解耦复杂逻辑,避免深度回调嵌套,并提高异步处理效率。理解这些细节有助于我们在实际项目中更有效地应用 EventProxy
。希望本文对你有所帮助,如果有任何疑问或建议,欢迎随时交流!
貌似相对 step没啥特别的优势呢?
step?不太了解您所谓的step概念,是否可以说得具体些?至于eventproxy本身就是一个轻量级的异步/基于事件的的模块,至于本身所具有的特点已经在文章中有所提及呢。当然自己也完全可以参考本思路,自己开发出一个基于异步/基于事件的模块出来!
谢谢分享,看完eventproxy源码之后,再看nodeclue就简单易懂了。
虽然说eventproxy可以用在前端,不过好像也不太适合前端用。
共同进步!!!
今天才看到这个帖子。太感谢楼主了。
当初看step的时候,还不支持并行。
可能前端的应用场景不太相同。但是如果用模块和事件较多的话,它就会体现出价值的。
要用Jscex哇!
楼上是老赵
我用的时候出现了这个问题:Error: db object already connecting, open cannot be called multiple times 望指教~ 参考他的readMe的例子
var ep = EventProxy.create("template", "data", "l10n", function (template, data, l10n) {
_.template(template, data, l10n);
});
$.get(“template”, function (template) {
// something
ep.emit(“template”, template);
});
$.get(“data”, function (data) {
// something
ep.emit(“data”, data);
});
$.get(“l10n”, function (l10n) {
// something
ep.emit(“l10n”, l10n);
});
似乎是因为我的get方法里面用了mongodb.open()这样的方法造成的不能多次打开数据库问题 我的代码:
var render = function (user, blogs) {
res.render("user", {title:user.name, blogs:blogs});
}
EventProxy.create("user", "blogs", render);
User.get(req.params.user, function (err, user) {
if (!user) {
req.flash("error", "用户不存在");
return res.redirect("/");
}
proxy.emit("user", user);
});
Blog.get(req.params.user, function (err, blogs) {
if (err) {
req.flash("error", err);
return res.redirect("/");
}
proxy.emit("blogs", blogs);
});
Blog.get 代码:
Blog.get = function get(username, callback) {
mongodb.open(function (err, db) {
if (err) {
return callback(err);
}
// 讀取 blogs 集合
db.collection('blogs', function (err, collection) {
//处理
});
});
};
User.get代码
User.get = function (username, callback) {
db.open(function (err, db) {
if (err) {
return callback(err);
}
db.collection("users", function (err, collection) {
//处理
});
});
}
这个有什么好的解决方法吗?
我想问一下关于代码最前面的这段是什么意思,能详细说说么……
;
(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();
}
})
比较想问下这个模块相对 async 有什么优势?async 还不需要自己去关注事件点,用起来更加省心~
之前我用的也是这个模块,但对个人来说有些不适,后来自己就开发了一套,部分内容比起 eventproxy 要好很多。
eventproxy
需要先创建,然后再 emit 触发,不是很灵活。从上面的例子可以看出来。
howdo
而 howdo 却不一样。 howdo.task().task().task() 链式创建有顺序的任务。 这些任务怎么执行?有2个 API ,一个是串行(follow),一个是并行(together)。 项目地址:https://www.npmjs.org/package/howdo 同时,howdo 也支持浏览器端的异步流程控制。
在 Node.js 中,EventProxy
模块通过事件机制解耦复杂业务逻辑,移除了深度回调嵌套问题,并将串行等待变为并行等待,从而提升多异步场景下的执行效率。该模块可以在 npm 上安装或直接从 GitHub 获取。
下面是 EventProxy
模块中的 _assign
方法的简化版实现,帮助理解其核心原理:
const EventProxy = require('eventproxy');
const ep = new EventProxy();
// 定义一个简单的异步函数
const asyncFunction = (eventName, delay, data) => {
setTimeout(() => {
ep.emit(eventName, data);
}, delay);
};
// 使用_eventProxy的assign方法
ep.assign(['event1', 'event2'], (data1, data2) => {
console.log(`Data received: ${data1}, ${data2}`);
});
// 发射事件
asyncFunction('event1', 1000, 'Hello');
asyncFunction('event2', 500, 'World');
在这段代码中,我们创建了一个 EventProxy
实例,并定义了一个简单的异步函数 asyncFunction
来模拟异步操作。然后,我们使用 ep.assign
方法将两个事件 event1
和 event2
绑定在一起,并在所有事件都触发后执行回调函数。
通过这种方式,我们可以轻松处理多个异步操作的结果,而无需担心回调地狱的问题。EventProxy
模块非常适合处理复杂的异步操作,并且可以提高代码的可读性和维护性。