天天看點

async和await是如何實作異步程式設計?

async和await是如何實作異步程式設計?

目錄

異步程式設計樣例

樣例解析

淺談Promise如何實作異步執行

參考

1.異步程式設計樣例

樣例:

// 等待執行函數

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.樣例解析

在樣例代碼中,test異步函數使用了async和await文法,這是ES2017裡面的異步程式設計規範。而為了在較低版本的浏覽器或Node支援這種文法,其中一種解決方案是将其轉化為Generator函數和Promise來實作。換句話說,任何的async和await實作的異步函數,都可以替換成Generator函數和Promise實作。

第一步:先将async和await文法替換為相應的Generator 函數,如下

// 代碼結構完全一緻,隻是替換了對應關鍵字

function *test() {

yield sleep(1000)

第二步:為了執行Generator 函數,使用Promise實作一個自動執行器函數 spawn

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)

asyncTest()

第四步:執行結果,與原來的一緻

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

執行到 spawn(test),進入spawn函數中,建立一個Promise并傳回。

執行到const gen = genF(),擷取一個狀态機。(Generator 函數是一個狀态機,封裝了多個内部狀态。)

執行到 step(function() { return gen.next(undefined); }), 進入step 函數中。

執行到next = nextF() ,next等于gen.next(undefined)的傳回結果。

當gen.next(undefined)開始執行,狀态機第一次調用,直到遇到第一個yield表達式為止,即yield sleep(1000),此時控制台先輸出"test start",并且傳回第一個狀态{ value: reuslt, done: false }, 而 reuslt等于sleep(1000)傳回的結果,其是一個Promise。

執行到if(next.done) ,此時第一個狀态的done為false,是以不執行if語句裡面,繼續往下執行。

執行到Promise.resolve(next.value),由于第一個狀态的value是一個Promise,是以直接傳回其本身,也就相當于執行sleep(1000).then(...),sleep函數異步等待1秒後,resolve接受的值為undefined,繼續執行then方法。

執行到 step(function() { return gen.next(v); }),此時v為undefined,再次進入step 函數中。

再次執行到next = nextF() ,next等于gen.next(undefined)的傳回結果。

當gen.next(undefined)再次執行時,狀态機第二次調用,此時Generator函數已經執行完畢,此時控制台先輸出"test end",并且傳回最後的狀态{ value: undefined, done: true }

執行到if(next.done) ,此時第一個狀态的done為true,是以執行if語句裡面。

執行到return resolve(next.value),此時最初的Promise成功執行。

至此asyncTest函數執行結束。

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

從上述樣例解析中可以看出,我們是用Promise來實作代碼的異步執行,那Promise的内部是如何實作異步執行的呢?

通過檢視Promise的源碼實作,發現其異步執行是通過asap這個庫來實作的。

asap是as 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

原文位址

https://www.cnblogs.com/forcheng/p/12668387.html

繼續閱讀