async、await和generator函數內部原理

對async、await的理解,內部原理

async 是Generator函數的語法糖,並對Generator函數進行了改進bash

  1. async的實現就是將Generator 函數和自動執行器,包裝在一個函數裏。
async function fn(args) {
  // ...
}

// 等同於

function fn(args) {
  return spawn(function* () {
    // ...
  });
}
複製代碼

spawn 函數就是自動執行器, 下面給出spawn函數的實現babel

function spawn(genF){
    return new Promise((resolve, reject)=>{
        const gen = genF() // 先將Generator函數執行下,拿到遍歷器對象
        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((v)=>{
                step(()=>{return gen.next(v)})
            }, (e)=>{
                step(()=>{return gen.throw(e)})
            })
        }
        step(()=> {return gen.next(undefinex)})
    })
}
複製代碼
  1. 更好的語義

async 和 await, 比起星號和yield,語義更加清楚,async表示函數裏面有異步操做,await表示緊跟在後面的表達式須要等待結果。多線程

  1. 更廣的適用性

co模塊約定,yield命令後面只能是Thunk函數或者Promise對象, 而async函數的await後面,能夠是Promise和原始類型值(數值、字符串和布爾值,但這時會自動轉成當即 resolved 的 Promise 對象, 查看spawn函數中Promise.resolve(next.value))閉包

  1. 返回值是Promise

比Generator函數的返回值是Iterator對象方便,可使用then方法指定下一步操做異步

你可能會問,async是Generator函數的語法糖,那什麼是Generator函數呢?

Generator函數語法:重點是*和關鍵字yieldasync

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
var hw = helloWorldGenerator();
複製代碼

執行Generator函數helloWorldGenerator的話,並不執行,而是返回一個迭代器對象,下一步,必須調用遍歷器對象的next方法,使得指針移向下一個狀態函數

hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }
複製代碼

yield 因爲 Generator 函數返回的遍歷器對象,只有調用next方法纔會遍歷下一個內部狀態,因此其實提供了一種能夠暫停執行的函數。yield表達式就是暫停標誌。工具

Generator與協程

協程是一種程序運行的方式,能夠用單線程實現,也能夠用多線程實現。ui

  • 協程概念:

一個線程(或函數)執行到一半,能夠暫停執行,將執行權交給另外一個線程(或函數),等到稍後收回執行權的時候,再恢復執行。這種能夠並行執行、交換執行權的線程(或函數),就稱爲協程。spa

  • 協程與普通線程的差別:
  1. 普通線程是搶先式的,會爭奪cpu資源,而協程是合做的,
  2. next 同一時間,能夠有多個普通線程運行,而協程則只有一個在運行,其餘協程則處在暫停狀態。

Generator 函數是 ES6 對協程的實現,可是不徹底,Generator 函數被稱爲「半協程」(semi-coroutine),意思是隻有 Generator 函數的調用者,才能將程序的執行權還給 Generator 函數。若是是徹底執行的協程,任何函數均可以讓暫停的協程繼續執行。

Generator 與上下文

Generator 函數, 它執行產生的上下文環境,一旦遇到yield命令,就會暫時退出堆棧,可是並不消失,裏面的全部變量和對象會凍結在當前狀態。等到對它執行next命令時,這個上下文環境又會從新加入調用棧,凍結的變量和對象恢復執行。

Generator 的實現

具體請參考alloyteam的文章

js的Generator並不是由引擎從底層提供額外的支持,而是經過代碼的編寫,來實現的函數暫停、按序執行。 在實現Generator過程當中有兩個關鍵點,一是要保存函數的上下文信息,二是實現一個完善的迭代方法,使得多個 yield 表達式按序執行,從而實現生成器的特性。 大致上就是使用由 switch case 組成的狀態機模型中, 除此以外,利用閉包技巧,保存生成器函數上下文信息。

Regenerator 經過工具函數將生成器函數包裝,爲其添加如 next/return 等方法。同時也對返回的生成器對象進行包裝,使得對 next 等方法的調用,最終進入由 switch case 組成的狀態機模型中。除此以外,利用閉包技巧,保存生成器函數上下文信息。

上述過程與 C#中 yield 關鍵字的實現原理基本一致,都採用了編譯轉換思路,運用狀態機模型,同時保存函數上下文信息,最終實現了新的 yield 關鍵字帶來的新的語言特性。

這個連接能夠查看轉換後的代碼:這個在線地址

相關文章
相關標籤/搜索