4.生成器控制流
支持生成器,迭代器的特殊情況,其中控制流可以暫停和恢復,以便支持與“協同例程”結合Promise的異步編程(見下文)。 [注意:通用異步函數通常由可重用的庫提供,這裏只是爲了更好地理解。 在實踐中見co或Bluebird的協程。]
傳統的編程語言,早有異步編程的解決方案(其實是多任務的解決方案)。其中有一種叫做"協程"(coroutine),意思是多個線程互相協作,完成異步任務。
協程有點像函數,又有點像線程。它的運行流程大致如下。
第一步,協程A開始執行。
第二步,協程A執行到一半,進入暫停,執行權轉移到協程B。
第三步,(一段時間後)協程B交還執行權。
第四步,協程A恢復執行。
上面流程的協程A,就是異步任務,因爲它分成兩段(或多段)執行。
舉例來說,讀取文件的協程寫法如下。
function* asyncJob() {
// ...其他代碼
var f = yield readFile(fileA);
// ...其他代碼
}
上面代碼的函數asyncJob是一個協程,它的奧妙就在其中的yield命令。它表示執行到此處,執行權將交給其他協程。也就是說,yield命令是異步兩個階段的分界線。
協程遇到yield命令就暫停,等到執行權返回,再從暫停的地方繼續往後執行。它的最大優點,就是代碼的寫法非常像同步操作,如果去除yield命令,簡直一模一樣。
—摘自ECMAScript 6 入門 阮一峯
ECMAScript 6
//通用異步控制流驅動程序
function async (proc, ...params) {
let iterator = proc(...params)
return new Promise((resolve, reject) => {
let loop = (value) => {
let result
try {
result = iterator.next(value)
}
catch (err) {
reject(err)
}
if (result.done)
resovle(result.value)
else if ( typeof result.value === "object" &&
typeof result.value.then === "function")
result.value.then((value) => {
loop()
},(err) => {
reject(err)
})
else
loop(result.value)
}
loop()
})
}
//特定的應用程序異步構建
function makeAsync (text, after) {
return new Promise((resolve, reject) =>{
setTimeout(() => resolve(text),after)
})
}
//特定的應用程序異步過程
async(function* (greeting) {
let foo = yield makeAsync("foo", 300)
let bar = yield makeAsync("bar", 200)
let baz = yield makeAsync("bar", 100)
return `${greeting} ${foo} ${bar} ${baz}`
},"Hello").then((msg) => {
console.log("RESULT:", msg) //"Hello foo bar baz"
})
ECMAScript 5
//ES5中沒有響應的表達方式
5.生成器方法
基於生成器函數支持生成器方法,即類和對象中的方法。
ECMAScript 6
class Clz {
* bar () {
...
}
}
let Obj = {
* foo () {
...
}
}
ECMAScript 5
//ES5中沒有響應的表達方式