async 是Generator函數的語法糖,並對Generator函數進行了改進bash
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)})
})
}
複製代碼
async 和 await, 比起星號和yield,語義更加清楚,async表示函數裏面有異步操做,await表示緊跟在後面的表達式須要等待結果。多線程
co模塊約定,yield命令後面只能是Thunk函數或者Promise對象, 而async函數的await後面,能夠是Promise和原始類型值(數值、字符串和布爾值,但這時會自動轉成當即 resolved 的 Promise 對象, 查看spawn函數中Promise.resolve(next.value))閉包
比Generator函數的返回值是Iterator對象方便,可使用then方法指定下一步操做異步
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表達式就是暫停標誌。工具
協程是一種程序運行的方式,能夠用單線程實現,也能夠用多線程實現。ui
一個線程(或函數)執行到一半,能夠暫停執行,將執行權交給另外一個線程(或函數),等到稍後收回執行權的時候,再恢復執行。這種能夠並行執行、交換執行權的線程(或函數),就稱爲協程。spa
Generator 函數是 ES6 對協程的實現,可是不徹底,Generator 函數被稱爲「半協程」(semi-coroutine),意思是隻有 Generator 函數的調用者,才能將程序的執行權還給 Generator 函數。若是是徹底執行的協程,任何函數均可以讓暫停的協程繼續執行。
Generator 函數, 它執行產生的上下文環境,一旦遇到yield命令,就會暫時退出堆棧,可是並不消失,裏面的全部變量和對象會凍結在當前狀態。等到對它執行next命令時,這個上下文環境又會從新加入調用棧,凍結的變量和對象恢復執行。
具體請參考alloyteam的文章
js的Generator並不是由引擎從底層提供額外的支持,而是經過代碼的編寫,來實現的函數暫停、按序執行。 在實現Generator過程當中有兩個關鍵點,一是要保存函數的上下文信息,二是實現一個完善的迭代方法,使得多個 yield 表達式按序執行,從而實現生成器的特性。 大致上就是使用由 switch case 組成的狀態機模型中, 除此以外,利用閉包技巧,保存生成器函數上下文信息。
Regenerator 經過工具函數將生成器函數包裝,爲其添加如 next/return 等方法。同時也對返回的生成器對象進行包裝,使得對 next 等方法的調用,最終進入由 switch case 組成的狀態機模型中。除此以外,利用閉包技巧,保存生成器函數上下文信息。
上述過程與 C#中 yield 關鍵字的實現原理基本一致,都採用了編譯轉換思路,運用狀態機模型,同時保存函數上下文信息,最終實現了新的 yield 關鍵字帶來的新的語言特性。
這個連接能夠查看轉換後的代碼:這個在線地址