带回调的异步并行行为

async 库具有一个名为 parallel 的便捷方法,该方法接受要运行的异步函数集合以及一个可选的回调函数,在所有函数成功完成后运行该回调函数。

根据文档,它的工作原理如下:

并行运行任务函数集合,无需等待前一个函数完成。如果任何函数将错误传递给其回调,则立即使用错误值调用主回调。任务完成后,结果将作为数组传递给最终回调。

然而,关于在某些用例下回调函数是否被调用,这可能会变得非常令人困惑,我在阅读带有回调地狱的大型遗留代码库时也感到很困惑。请参阅下面的代码和示例输出。

  • 所有任务都运行,但不执行回调
import async from 'async';

const task = {
  A: true,
  B: true,
};

async.parallel(
  [
    function (done) {
      if(task.A) {
        console.log('does A');
        return;
      }
      done(null);
    },
    function (done) {
      if(task.B) {
        console.log('does B');
        return;
      }
      done(null);
    },
  ],
  function (err) {
    if (err) {
      console.log(err);
      return;
    }
    console.log('does callback');
  },
);

/* console output
does A
does B
*/
  • 一个任务运行,但没有回调
import async from 'async';

const task = {
  A: true,
  B: false,
};

async.parallel(
  [
    function (done) {
      if(task.A) {
        console.log('does A');
        return;
      }
      done(null);
    },
    function (done) {
      if(task.B) {
        console.log('does B');
        return;
      }
      done(null);
    },
  ],
  function (err) {
    if (err) {
      console.log(err);
      return;
    }
    console.log('does callback');
  },
);

/* console output
does A
*/
  • 无任务运行然后回调运行
import async from 'async';

const task = {
  A: false,
  B: false,
};

async.parallel(
  [
    function (done) {
      if(task.A) {
        console.log('does A');
        return;
      }
      done(null);
    },
    function (done) {
      if(task.B) {
        console.log('does B');
        return;
      }
      done(null);
    },
  ],
  function (err) {
    if (err) {
      console.log(err);
      return;
    }
    console.log('does callback');
  },
);

/* console output
does callback
*/
  • 一个任务通过回调运行
import async from 'async';

const task = {
  A: true,
  B: false,
};

async.parallel(
  [
    function (done) {
      if(task.A) {
        setTimeout(() => {
          console.log('does A');
          done(null);
        }, 100);
        return;
      }
      done(null);
    },
    function (done) {
      if(task.B) {
        console.log('does B');
        return;
      }
      done(null);
    },
  ],
  function (err) {
    if (err) {
      console.log(err);
      return;
    }
    console.log('does callback');
  },
);

/* console output
does A
does callback
*/

我们可以看到,只有当所有并行函数最终正常调用时,callback 才会被调用。至于错误情况,文档很清楚:

如果任何函数将错误传递给其回调,则会立即使用错误的值调用主回调。

参考资料

nodejs javascript