node.js 多個非同步過程判斷執行是否完成

will233發表於2019-02-16

場景:想請求量較大的網路資料,比如想獲取1000條結果,但資料處理速度慢,有超時的風險,可以分成10次處理,每次處理100條;所有請求完成後再統一進行處理。

這樣的應用場景,可以這樣處理:

方案一:判斷請求到的資料條目

// 模擬網路請求
function fetch(url, callback) {
  setTimeout(function (){
    callback(null, {
        subjects: [{
          data: Math.round(Math.random() * 100)
        }]
      });
  }, 2000);
}

// 實現方案1
function multiTask_1 () {
  var arr = [];
  var baseUrl = `https://api.douban.com/v2/movie/top250`;
  for (var start = 0; start < 10; start++) {
    var url = baseUrl + `?start=` + start + "&count=1";
      fetch(url, function(error, res) {
        var data = res.subjects;
        arr = arr.concat(data);
        // 呼叫完成後統一處理
        if (arr.length === 10) {
          console.log(arr);
        }
    });
  }
}

將執行結果用arr.length來判斷,如果arr.length不像我們期望的那樣,比如由於網路傳輸或者處理異常,少一條,那麼我們將無法做後續的處理。這種處理方式強業務耦合;不具有普適性。

方案二:判斷非同步過程執行次數

// 方案2
function multiTask_2 () {
  var taskWatcher = 0;
  var arr = [];
  var baseUrl = `https://api.douban.com/v2/movie/top250`;
  for (var start = 0; start < 10; start++) {
    taskWatcher++;
    var url = baseUrl + `?start=` + start + "&count=1";
    fetch(url, function(error, res) {
        var data = res.subjects;
        arr = arr.concat(data);
        taskWatcher--;
        if (taskWatcher === 0) {
          console.log(arr);
        }
    });
  }
}

方案2 的判斷條件,這裡的 taskWatcher 充當非同步任務執行情況的觀察員,
僅與非同步過程的呼叫次數有關,且與其他處理過程無關。那有沒有其他方案呢

方案三:Promise.all()

Promise.all(iterable) 方法返回一個 Promise, 它將在上述可迭代物件中的所有 Promise 被 resolve 之後被 resolve,或者在任一 Promise 被 reject 後被 reject。

function multiTask_3 () {
  // var taskWatcher = 0;
  var taskStack = [];
  var arr = [];
  var baseUrl = `https://api.douban.com/v2/movie/top250`;
  for (var start = 0; start < 10; start++) {
    taskStack.push(
      new Promise((resolve, reject) => {
        var url = baseUrl + `?start=` + start + "&count=1";
        fetch(url, function(error, res) {
          var data = res.subjects;
          arr = arr.concat(data);
          resolve();
        });
      })
    );
  }

  Promise.all(taskStack).then(function () {
    console.log(arr);
  });
}

這種方式更具有通用性,如果非同步任務型別不同,也可以用這種方式來解決。不過應當注意reject的處理。避免其對最終處理的影響。

方案四: EventProxy

EventProxy是樸靈寫的,https://github.com/JacksonTian/eventproxy

  var ep = new EventProxy();
  var arr = [];
  ep.after(`fetchData`, 10, function (list) {
    list.forEach(function(item){
      arr = arr.concat(item); 
    });
    console.log(arr);
  });

  var baseUrl = `https://api.douban.com/v2/movie/top250`;
  for (var start = 0; start < 10; start++) {
    var url = baseUrl + `?start=` + start + "&count=1";
      fetch(url, function(error, res) {
        var data = res.subjects;
        ep.emit(`fetchData`, data);
    });
  }
  

EventProxy基於事件訂閱/釋出模式,這裡的after 方法可以偵聽多次事件,回撥中儲存了多次非同步任務的資料結果的陣列;除此之外EventProxy還支援多個不同事件的偵聽和處理。

相關文章