转载

Node.js 异步编程之 Callback 的问题

在上一篇中,我们使用 Callback 的方式来实现了我们需求,将一个 IP 列表转换成了具体的城市和天气。可以看出回调嵌套并不是 Callback 作为异步处理方案的真真问题。那正真的问题是什么呢?

可靠性

上篇文章发出来,就被@朴灵 吐槽,我还浑然不知。看上一篇文章中的这段代码:

try {   data = JSON.parse(data)   callback(null, data) } catch (error) {   callback(error) } 

事实上,在这段代码中, callback 有可能被调用两次。

这个问题 苏千在12年的沪JS大会上已经讲过了 ,我是现场观众,但却忘记了……

如果 JSON.parse 成功,但是 callback 在运行的时候报异常的话,就会触发 catch 块, callback 就会再被调用一次。这个问题不难理解,但是非常隐蔽。下面是可行的一种 fix 方案:

var hasError = false try {   data = JSON.parse(data) } catch (e) {   err = e   hasError = true } if (hasError) {   callback(err) } else {   callback(null, data) } 

其实不仅仅是上面这段代码,看下面这一段:

for (var i = 0; i < ips.length; i++) {   ip = ips[i];   (function(ip) {     ip2geo(ip, function(err, geo) {       if (err) {         callback(err)       } else {         geo.ip = ip         geos.push(geo)         remain--       }       if (remain == 0) {         callback(null, geos)       }     })   })(ip) } 

这段代码来自 ips2geos 函数,这个函数就是实现并行地异步读取多个 IP 地址的 geo 数据,读取成功后组装成数组返回给 callback ;但如果某个异步读取过程出错了,就直接调用 callback 将错误信息返回。但在这段代码中, callback 很可能被调用多次, 这种情况出现在有多个异步 IP 转 geo 出错的时候 。一种还算凑活的修正:

var returned = false for (var i = 0; i < ips.length; i++) {   ip = ips[i];   (function(ip) {     ip2geo(ip, function(err, geo) {       if (returned) {         return       }       if (err) {         callback(err)         returned = true       } else {         geo.ip = ip         geos.push(geo)         remain--       }       if (remain == 0) {         callback(null, geos)       }     })   })(ip) } 

这就是 callback 的可靠性问题。每个以 callback 作为异步回调逻辑都可能产生问题。我们自己写的代码,或者第三方类库都有可能导致 callback 被重复调用。以 callback 提供的异步 API 是无法保证回调次数的,这就产生了信任问题。如果有大量的异步嵌套,只要出错,就是一场灾难。

很难处理串/并行异步操作

串并行的异步操作大大提高了程序的复杂度,而直白的 callback 拿这个问题没有太大的办法。

作为写程序的开发者,同步逻辑更容易理解,更直观。可以像下面这样:

串行逻辑:

var ips = readIP('./ip.json') var geos = ips2geos(ips) var weathers = geos2weathers(geos) ... 

并行逻辑:

function ips2geos(ips) {   var geos = []   var ip   for (var i = 0; i < ips.length; i++) {     ip = ips[i]     geos.push(ip2geo(ip))   }   return geos } 

很简单不是。

像上面这样的代码, fibjs 可以做到,fibjs 把异步串/并行做到了自己的内部实现中。

异步打破了程序运行的正常顺序,而 callback 的表现力非常不足,稍微复杂的处理逻辑代码写起来就一团糟。见第一节的 ips2geos 等函数。

总结

在本文中我们指出了 callback 作为异步处理的两个比较严重的问题,异步本身并不是坏事,只是 callback 的方案缺乏可靠性,表现力不足。在下一篇文章中我就进入正题,开始给大家介绍 thunk 以及 thunks 类库。后者是@严清 开发的一个类库,灵感来自于 co,意在提升异步编程的体验。敬请期待。

正文到此结束
Loading...