JavaScript 並行和串行任務/流程控制 的實現

JavaScript 並行和串行任務

在 JavaScript 裏面 異步 是一個很重要的知識點,JS 的異步跟其餘語言不同, 他是根據執行回調的方式來 實現的。因爲咱們不知道異步何時會執行完,讓一組異步任務順序執行就顯得很重要了,這個時候咱們就須要 流程控制 ,這種流程控制有兩種 串行(series)並行(parallel)git

圖片描述

實際開發中也許你在 JS 中不多接觸到這個 並行串行 ,可是若是你接觸過 gulp 我相信你應該對這個概念並不陌生.由於在 gulp 裏面編寫 task 處處充斥着這種異步執行。在 gulp(4.0)以前,執行任務想要實現這個流程控制只能經過 依賴 來實現或者經過 run-sequence,而在 gulp(4.0)以後官方本身實現了 gulp.seriesgulp.parallel 來實現。若是以前沒有接觸過 gulp 有興趣的能夠了解下(雖然如今是 Webpack 的世界~~~)github

而若是對於 串行並行 有所接觸的話,通常都會使用一個叫 nimble,可是我我的對於這個庫裏面實現 並行 功能是有疑問的.疑問點在於他不是按照我想象的工做方式工做npm

串行的簡單實現

串行的實現有點像 Koa 裏面對於中間件的實現。經過一個 next 函數來觸發下一個函數的執行。gulp

// 或者使用reduce
let series = function(arr) {
  let index = 0;
  let next = function() {
    if (index >= arr.length) {
      return;
    }
    arr[index](next);
    index++;
  };
  next();
};

這是一個基本的實現邏輯,實現的方式有點粗糙,其實咱們每次取出的函數的方式能夠進行一個優化,不須要中間變量 index 來記錄當前的執行函數,而是經過 Array.prototype.shift 去取數組裏面的函數用來執行數組

let series = function(arr) {
  let next = function(data) {
    let fn = arr.shift();
    if (!fn) {
      return;
    }
    fn(next,data);
  };
  next();
};

// 使用方法
let str = "";
series([
  function(next) {
    setTimeout(function() {
      str = "Hello"
      next(str);
    }, 100);
  },
  function(next,data) {
    setTimeout(function() {
      str += "World"
      console.log(str)
      console.log(data);
      next();
    });
  }
]);

並行的簡單實現

剛剛在上面提到過,我對於 nimble 中 並行的實現是有疑問的,他並無按照我上面的 圖片執行,少了我以爲很重要的一步 並行應該又一個最終的函數,這個函數要等其餘函數執行完畢纔會執行異步

假設我有三個須要並行執行的函數 fn1,fn2,fn3。 當這三個函數執行以後應該須要在時間最晚的函數內執行一個最終的函數,來 處理一些依賴邏輯。下面是我我的的 簡單實現代碼async

let parallel = function(arr, finnaly) {
  let fn,
    index = 0;
  let statusArr = Array(arr.length)
    .fill()
    .map(() => ({
      isActive: false,
      data: null
    }));
  let isFinished = function() {
    return statusArr.every(item => {
      return item.isActive === true;
    });
  };
  let resolve = function(index) {
    return function(data) {
      statusArr[index].data = data;
      statusArr[index].isActive = true;
      let isFinish = isFinished();
      if (isFinish) {
        let datas = statusArr.map(item => {
          return item.data;
        });
        finnaly(datas);
      }
    };
  };
  while ((fn = arr.shift())) {
    // 給resolve函數追加參數,可使用bind函數實現,這裏使用了柯里化
    fn(resolve(index));
    index++;
  }
};

// 使用方法
let str = "";
parallel(
  [
    function(resolve) {
      setTimeout(function() {
        str = "Hello";
        resolve("Hello");
      }, 1000);
    },
    function(resolve) {
      setTimeout(function() {
        str += "World";
        resolve("World");
      }, 20);
    }
  ],
  function(datas) {
    console.log("finily", datas);
  }
);

關於其餘

以上就是簡單的 串行並行 實現的基本方案, 有一些地方用到了 ES6,但願讀者不要介意。其實若是你是 面向ES6開發 的話。更簡單的方式是用 Promise 的方案,甚至可使用 async/awaitgenerator 這些高級方法,來使 異步 代碼跟同步同樣使用,使代碼的可讀性更高函數

// 並行
let datas = await Promise.all([
  new Promise(resolve => {
    setTimeout(resolve.bind(this,10), 1000);
  }),
  new Promise(resolve => {
    setTimeout(resolve.bind(this,12), 2000);
  })
])

// 串行
let data1 = await new Promise(resolve => {
  setTimeout(resolve.bind(this, 10), 1000);
})
let data2 = await new Promise(resolve => {
  setTimeout(resolve.bind(this, 12), 1000);
});

這樣看起來是否是很簡潔優化

ES6 大法好!!!

ES6 大法好!!!

ES6 大法好!!!

重要事情說三遍
相信你看出來了吧,這篇文章實際上是來宣傳ES6的,不過仍是但願你們能瞭解一下底層的實現。另外有一篇 Promise的實現。寫的至關好,極力推薦閱讀一下this

圖片描述

相關文章
相關標籤/搜索