Page 144 - Node.js开发指南
P. 144
6.2 控制流 137
上面代码在 for 循环体中建立了一个匿名函数,将循环迭代变量 i 作为函数的参数传 1
递并调用。由于运行时闭包的存在,该匿名函数中定义的变量(包括参数表)在它内部的函
数( fs.readFile 的回调函数)执行完毕之前都不会释放,因此我们在其中访问到的 i 就
分别是不同的闭包实例,这个实例是在循环体执行的过程中创建的,保留了不同的值。
事实上以上这种写法并不常见,因为它降低了程序的可读性,故不推荐使用。大多数情 2
况下我们可以用数组的 forEach 方法解决这个问题:
//callbackforeach.js
var fs = require('fs'); 3
var files = ['a.txt', 'b.txt', 'c.txt'];
files.forEach(function(filename) {
fs.readFile(filename, 'utf-8', function(err, contents) {
console.log(filename + ': ' + contents); 4
});
});
6.2.2 解决控制流难题 5
除了循环的陷阱,Node.js 异步式编程还有一个显著的问题,即深层的回调函数嵌套。
在这种情况下,我们很难像看基本控制流结构一样一眼看清回调函数之间的关系,因此当程
序规模扩大时必须采取手段降低耦合度,以实现更加优美、可读的代码。这个问题本身没有 6
立竿见影的解决方法,只能通过改变设计模式,时刻注意降低逻辑之间的耦合关系来解决。
除此之外,还有许多项目试图解决这一难题。async 是一个控制流解耦模块,它提供了
async.series、async.parallel、async.waterfall 等函数,在实现复杂的逻辑时使
用这些函数代替回调函数嵌套可以让程序变得更清晰可读且易于维护,但你必须遵循它的编 7
程风格。
streamlinejs和jscex则采用了更高级的手段,它的思想是“变同步为异步”,实现了一个
JavaScript 到JavaScript 的编译器,使用户可以用同步编程的模式写代码,编译后执行时却是
异步的。 8
eventproxy 的思路与前面两者区别更大,它实现了对事件发射器的深度封装,采用一种
完全基于事件松散耦合的方式来实现控制流的梳理。
无论是以上哪种解决手段,都不是“非侵入性的”,也就是说它对你编程模式的影响是 9
非常大的,你几乎不可能无代价地在使用了一种模式很久以后从容地换成另一种模式,或者
直接糅合使用两种模式。而且它们都是在解决了深层嵌套的回调函数可读性问题的同时,引
入了其他复杂的语法,带来了另一种可读性的降低。所以,是否使用,使用哪种方案,在决
定之前是需要仔细斟酌研究的。 10