需求场景:某一业务逻辑要求加载三个相互之间不关联的资源 一个磁盘上的文件,读取数据库的数据,并且取得某个远程API的返回数据。而我们的处理要等待这三个资源全部加载完毕后才进行处理。
不同于传统的编程语言,在同步编程模型下这样的代码很好的,三行加截,第四行判断,然后就可以开始写处理逻辑了。花费的时间是 a + b + c
1
2
3
4
5
|
var file = File.readAll("d:/test.txt");
var data = Data.selectAll("SELECT * FROM ...");
var list = Api.getList("http://www.api-example.com/api");
if (file && data && list)
// to do process;
|
而Node.js 的I/O库都是异步设计,那么我们可能会这样写:
1
2
3
4
5
6
7
8
9
10
|
fs.readFile("d:/test.txt", "utf-8", function(err, content) {
if (err) return handleErr(err);
db.query("SELECT * FROM ...", function(err, result) {
if (err) return handleErr(err);
api.getList("http://www.api-example.com/api", function(err, content) {
if (err) return handleErr(err);
// to do process;
}
}
}
|
这样代码虽然可以正常工作,但花费的时间跟同步代码是一样的,并且嵌套太多,代码维护会很不方便。我们加载的三个资源中间并没有前后依赖的关系,因此更好的处理方法是同时对这三个资源进行加载,在 最后一个资源加载成功后执行我们的处理操作。这样一来,花费的时间便成了:max(a, b, c) 利用 EventProxy 可以很简单地完成这样的任务:
1
2
3
4
5
6
7
8
9
10
11
|
var ep = new EventProxy();
ep.all('file', 'data', 'list' // 声明所有需要加载的资源名称
, function(file, data, list) { // 处理函数,参数列表与资源名称相对应。
// to do process
}
);
ep.fail(handleErr);
fs.readFile('file', ep.done('file'));
db.query('SELECT * FROM ..', ep.done('data'));
api.getList("http://www.api-example.com/api", ep.done("list"));
|
这样一样整个程序结构清晰很多,也不必每次对 err 进行判断处理。
EventProxy 的实现得益于 Node.js 的异步库标准化处理方式, Node.js 的异步IO库都会接收一个 callback 函数作为回调,根据约定, callback 函数第一个参数是 err,第二个参数就是加载完成的结果, ep.done 是一个偏函数,它会返回一个函数,这个函数帮你判断是否有错误发生,如果有的话则触发 fail 事件,那么我们的 handleErr 函数就会被执行,如果所有的资源都成功加载则触发 all 事件,这时候我们在 all 中传入的回调函就会被执行, 同时相应的资源也会被作为参数传入到我们的函数中。