帶回調的非同步平行行為

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
*/

我們可以看到,只有當它最終從所有並行函數中正常呼叫時,才會呼叫「回調」。至於錯誤情況,文檔很清楚:

如果任何函數將錯誤傳遞給其回調,則會立即使用錯誤的值呼叫主回調。

參考文獻

nodejs javascript