简化版 async.parallel [bug fixed]
发布于 3年前 作者 guilin 2173 次浏览

async.js 是我最喜欢的异步控制模块, 主要原因是用 async 写出来的代码相对如用 step.js 写出来的代码可读性更强. async.parallel, async.waterfall 这些API的命名也很形象.

近日想在浏览器端并发进行多个ajax请求, 于是便想把 async.js 放在浏览器端代码中, 可是一看源码才发现文件相对于我的需求而言–略大, 只好自己写个简化版的只支持 async.parallel.

1L 2L 提到的bug已经修复

https://gist.github.com/2016922

// This is an lite version of `async`, see https://github.com/caolan/async
//
// es5shim.js is required for old version browsers
//
// Author: Gui Lin
// Email: guileen[@gmail](/user/gmail).com


var async = {};

if (typeof module !== 'undefined' && module.exports) {
  module.exports = async;
}

// example:
//
// async.parallel([
//     function(_callback) {
//       loadUser(uid, _callback);
//     }
//   , function(_callback) {
//       loadBook(bookId, _callback);
//     }
//   ], function(err, results) {
//     var user = results[0]
//       , book = results[1]
//       ;
//     console.log(user);
//     console.log(book);
// })

async.parallel = function (tasks, callback) {
  var results = [], count = tasks.length;
  tasks.forEach(function(task, index) {
      task(function(err, data) {
          results[index] = data;
          if(err) {
            callback(err);
            callback = null;
          }
          if(--count === 0 && callback) {
            callback(null, results);
          }
      });
  });
}
9 回复

这个实现是有问题的。

async.parallel 应当是当所有的异步任务都执行完毕时才调用最终的回调函数,而你的实现则是当数组中的最后一个任务执行完毕时就调用最终的回调函数,而此时前面的任务不一定执行完了。

考虑下面这个例子:

var a = function (callback) {
  setTimeout(function() {
    callback(null, 1);
  }, 200);
};

var b = function (callback) {
  setTimeout(function() {
    callback(null, 2);
  }, 100);
};

async.parallel([a, b], function(err, results) {
  console.log(results[0] + results[1]);
});

函数 a 在 200 毫秒后执行完毕,而函数 b 在 100 毫秒后执行完毕,由于你的实现是当数组中的最后一个任务执行完毕时就回调了,此时 a 任务还没有完成,所以最终 log 下来的值为 undefined + 2,即 NaN

正确的做法应当是维护一个计数器,用于统计当前已经完成的任务数目,当全部任务都完成时才调用最终的回调函数。

维护一个计数器的话,每个任务要重置这个计数器,如果一个任务失败了,如何处理?容错呢? 再改一下就又回到async了

async 的做法是当其中一个任务失败的时候就直接callback(err),并将每个任务的回调函数设为空函数:function() {},以使剩余任务的回调无效化。

另外,其实 async 本来就很轻量(压缩后只有 1.7 KB),只是因为它在试图最大化的对逻辑进行抽象,以尽可能的重用现有的代码,所以导致其源码看上去有些复杂罢了。

@xqunix

  • js不精,我也大体看了下源码,基本上这些库的思路都是在array功能(filter,map,every…)之上做了针对于func的封装
  • "以使剩余任务的回调无效化",其实其它回调我还是可以获得返回值的,只要callback在那个错误之前

.

多谢提醒.

已经修改了.

修改了一下, 你提到的这个问题应该修复了

同喜欢async.js!!!

回到顶部