如何从许多内部非阻塞函数调用之一中提前退出外部函数?

How to exit outer function early from one of many inner non-blocking function calls?

我正在编写 async.parallel 的简单实现作为学习练习。这是文档中的描述:

parallel(tasks, [callback]) Run the tasks array of functions in parallel, without waiting until the previous function has completed. If any of the functions pass an error to its callback, the main callback is immediately called with the value of the error. Once the tasks have completed, the results are passed to the final callback as an array.

我不确定如何提前退出,以防其中一个函数returns出错。我正在向回调传递一个错误,但其他函数当然会继续执行。

以下是我目前所拥有的;欢迎在您的机器上试用。

function parallel(tasks, cb) {
  cb = cb || function() {};

  if (!Array.isArray(tasks)) return cb('tasks must be an array');  
  if (!tasks.length) return cb();

  var res = [];
  var completed_count = 0;

  for (var i=0; i<tasks.length; i++) {
    (function(ind) {
      tasks[ind](function(err, val) {
    if (err) return complete(err); // <--- ! 
    res[ind] = val;
    completed_count++;
    if (completed_count === tasks.length) complete(null, res);
      });
    } (i));
  }
};


// ===== test functions =====
function slow(time, cb) {
  setTimeout(function() {
    cb(null, time);
  }, time);
};

function slowError(time, cb) {
  setTimeout(function() {
    cb('Some Error', time);
  }, time);
}

function complete(err, results) {
  if (err) return console.log(err);
  else return console.log(results);
};


// ===== tests =====
// should return [1000, 2000, 3000]
parallel([slow.bind(null, 1000),
          slow.bind(null, 2000),
          slow.bind(null, 3000)], complete);

// should exit early
parallel([slowError.bind(null, 1000),
          slowError.bind(null, 2000),
          slow.bind(null, 3000)], complete);

您必须将整个执行的状态存储在 parallel 函数中,并在每个回调中检查:应该是 errored/finished 状态中的状态,每个下一个回调都应该被取消。像这样:

function parallel(tasks, cb) {
  var result = [];
  var await = tasks.length;
  tasks.forEach(function(task, i){
    task(function(err, val){
      if (await === 0) return;
      if (err) {
        await = 0;
        cb(err);
        return;
      }
      result[i] = val;
      if (--await === 0) cb(null, result);
    })
  });
}

这里我使用倒计时来匹配结束或完成状态。例如,您还可以使用 errored 布尔标志,或任何您想要保存状态并在每次回调时检查它的东西。

but of course the other functions continue executing

是的。而且您对此无能为力,因为它们没有为您提供取消它们的方法。 async.js 也什么都不做,你只需要让他们 运行.

I'm not sure how to exit early

但是 "letting them run" 并不意味着您必须等待他们。您可以立即触发回调 - 这就是他们正在谈论的 "immediate" 出口。


在您的实施中有些地方您没有完全正确:

  • 确保以绝对不可以的方式多次调用 callback。用户希望你做到这一点,如果你做不到,他们的应用程序将 运行 发疯。
  • 你应该能够处理也不能正确完成的任务,并多次调用你的函数表达式
  • 重申第 1 点:应该忽略更多错误
  • 您可能希望在完成后防止内存泄漏和垃圾回收结果数组,即使某些任务仍然保留对您的函数表达式的引用

我会把这些作为练习留给你:-)