async和await是如何實現異步編程?

目錄git

  • 異步編程樣例
  • 樣例解析
  • 淺談Promise如何實現異步執行
  • 參考

1.異步編程樣例es6

樣例:github

// 等待執行函數
function sleep(timeout) {
  return new Promise((resolve) => {
    setTimeout(resolve, timeout)
  })
}

// 異步函數
async function test() {
  console.log('test start')
  await sleep(1000)
  console.log('test end')
}

console.log('start')
test()
console.log('end')

執行結果:編程

start
test start
end
test end

2.樣例解析promise

在樣例代碼中,test異步函數使用了asyncawait語法,這是ES2017裏面的異步編程規範。而爲了在較低版本的瀏覽器或Node支持這種語法,其中一種解決方案是將其轉化爲Generator函數和Promise來實現。換句話說,任何的asyncawait實現的異步函數,均可以替換成Generator函數和Promise實現。瀏覽器

第一步:先將asyncawait語法替換爲相應的Generator 函數,以下異步

// 代碼結構徹底一致,只是替換了對應關鍵字
function *test() {
  console.log('test start')
  yield sleep(1000)
  console.log('test end')
}

第二步:爲了執行Generator 函數,使用Promise實現一個自動執行器函數 spawnasync

function spawn(genF) {
  return new Promise(function(resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}

第三步:將相應的Generator 函數和自動執行器函數相結合,便可得最終的等價代碼異步編程

function asyncTest() {
  spawn(test)
}

console.log('start')
asyncTest()
console.log('end')

第四步:執行結果,與原來的一致函數

start
test start
end
test end

第五步:分析asyncTest函數執行過程

  1. 執行到 spawn(test),進入spawn函數中,建立一個Promise並返回。
  2. 執行到 const gen = genF(),獲取一個狀態機。(Generator 函數是一個狀態機,封裝了多個內部狀態。)
  3. 執行到 step(function() { return gen.next(undefined); }), 進入step 函數中。
  4. 執行到next = nextF()next等於gen.next(undefined)的返回結果。
    • gen.next(undefined)開始執行,狀態機第一次調用,直到遇到第一個yield表達式爲止,即yield sleep(1000),此時控制檯先輸出"test start",而且返回第一個狀態{ value: reuslt, done: false }, 而 reuslt等於sleep(1000)返回的結果,其是一個Promise。
  5. 執行到if(next.done) ,此時第一個狀態的donefalse,因此不執行if語句裏面,繼續往下執行。
  6. 執行到Promise.resolve(next.value),因爲第一個狀態的value是一個Promise,因此直接返回其自己,也就至關於執行sleep(1000).then(...),sleep函數異步等待1秒後,resolve接受的值爲undefined,繼續執行then方法。
  7. 執行到 step(function() { return gen.next(v); }),此時vundefined,再次進入step 函數中。
  8. 再次執行到next = nextF()next等於gen.next(undefined)的返回結果。
    • gen.next(undefined)再次執行時,狀態機第二次調用,此時Generator函數已經執行完畢,此時控制檯先輸出"test end",而且返回最後的狀態{ value: undefined, done: true }
  9. 執行到if(next.done) ,此時第一個狀態的donetrue,因此執行if語句裏面。
  10. 執行到return resolve(next.value),此時最初的Promise成功執行。
  11. 至此asyncTest函數執行結束。

3.淺談Promise如何實現異步執行

從上述樣例解析中能夠看出,咱們是用Promise來實現代碼的異步執行,那Promise的內部是如何實現異步執行的呢?

經過查看Promise源碼實現,發現其異步執行是經過asap這個庫來實現的。

asapas soon as possible的簡稱,在Node和瀏覽器環境下,能將回調函數以高優先級任務來執行(下一個事件循環以前),即把任務放在微任務隊列中執行。

宏任務(macro-task)和微任務(micro-task)表示異步任務的兩種分類。在掛起任務時,JS 引擎會將全部任務按照類別分到這兩個隊列中,首先在 macrotask 的隊列(這個隊列也被叫作 task queue)中取出第一個任務,執行完畢後取出 microtask 隊列中的全部任務順序執行;以後再取 macrotask 任務,周而復始,直至兩個隊列的任務都取完。

用法:

asap(function () {
    // ...
});

4.參考

ECMAScript 6 入門 - async 函數

【翻譯】Promises/A+規範

Promise - Bare bones Promises/A+ implementation

相關文章
相關標籤/搜索