異步無處不在:Generator 異步(五)
(????)??嗨,我是你穩(wěn)定更新、持續(xù)輸出的勾勾。

關(guān)于異步的知識點,終于要結(jié)束了!
本篇就來重點說說如何將 Generator 寫進(jìn)異步中。
Generator 異步方案
將調(diào)用 ajax 的代碼寫到生成器函數(shù)的 yield 后面,每次的異步執(zhí)行,都要在 yield 中暫停,調(diào)用的返回結(jié)果是一個 Promise 對象。
我們可以從迭代器對象的 value 屬性獲取到 Promise 對象,然后使用 .then 進(jìn)行鏈?zhǔn)秸{(diào)用處理異步結(jié)果。
結(jié)果處理的代碼叫做執(zhí)行器,就是具體負(fù)責(zé)運(yùn)行邏輯的代碼。
function ajax(url) {……}// 聲明一個生成器函數(shù)function * fun(){yield myAjax('./d1.json')yield myAjax('./d2.json')yield myAjax('./d3.json')}// 返回 遍歷器對象var f = fun();// 生成器函數(shù)的執(zhí)行器// 調(diào)用 next 方法,執(zhí)行異步代碼var g = f.next();g.value.then(data=>{console.log(data);// console.log(f.next());g = f.next();g.value.then(data=>{console.log(data)// g.......})})
而執(zhí)行器的邏輯中,是相同嵌套的,因此可以寫成遞歸的方式對執(zhí)行器進(jìn)行改造:
//?聲明一個生成器函數(shù)function * fun(){yield myAjax('./d1.json')yield myAjax('./d2.json')yield myAjax('./d3.json')}// 返回 遍歷器對象var f = fun();// 遞歸方式 封裝// 生成器函數(shù)的執(zhí)行器function handle(res){if(res.done) return;res.value.then(data=>{console.log(data)handle(f.next())})}handle(f.next());
然后,再將執(zhí)行的邏輯,進(jìn)行封裝復(fù)用,形成獨(dú)立的函數(shù)模塊。
function co(fun) {// 返回 遍歷器對象var f = fun();// 遞歸方式 封裝// 生成器函數(shù)的執(zhí)行器function handle(res) {if (res.done) return;res.value.then(data => {console.log(data)handle(f.next())})}handle(f.next());}co(fun);
封裝完成后,我們再使用時只需要關(guān)注 Generator 中的 yield 部分就行了。
function co(fun) {……}function * fun(){yield myAjax('./d1.json')yield myAjax('./d2.json')yield myAjax('./d3.json')}
此時你會發(fā)現(xiàn),使用 Generator 封裝后,異步的調(diào)用就變得非常簡單了。
但是,這個封裝還是有點麻煩,有大神幫我們做了這個封裝,相當(dāng)強(qiáng)大:https://github.com/tj/co ,感興趣可以研究一下。
而隨著 JS 語言的發(fā)展,更多的人希望類似 co 模塊的封裝能夠?qū)戇M(jìn)語言標(biāo)準(zhǔn)中,而我們直接使用這個語法規(guī)則就行了。
其實你也可以對比一下,使用 co 模塊后的 Generator 和 async 這兩段代碼:
// async / awaitasync function callAjax(){var a = await myAjax('./d1.json')console.log(a);var b = await myAjax('./d2.json');console.log(b)var c = await myAjax('./d3.json');console.log(c)}// 使用 co 模塊后的 Generatorfunction * fun(){yield myAjax('./d1.json')yield myAjax('./d2.json')yield myAjax('./d3.json')}
你應(yīng)該也發(fā)現(xiàn)了,async 函數(shù)就是 Generator 語法糖,不需要自己再去實現(xiàn) co 執(zhí)行器函數(shù)或者安裝 co 模塊。
寫法上將 * 星號去掉換成放在函數(shù)前面的 async,把函數(shù)體的 yield 去掉,換成 await。
簡直完美!
async function callAjax(){var a = await myAjax('./d1.json')console.log(a);var b = await myAjax('./d2.json');console.log(b)var c = await myAjax('./d3.json');console.log(c)}callAjax();
我們再來看一下 Generator。
相信下面的代碼,你能很輕松地閱讀。
function * f1(){console.log(11)yield 2;console.log('333')yield 4;console.log('555')}var g = f1();g.next();console.log(666);g.next();console.log(777);
代碼運(yùn)行結(jié)果:

帶著 Generator ?的思路,我們再回頭看看那個 async 的面試題。
請寫出以下代碼的運(yùn)行結(jié)果:
setTimeout(function () {console.log('setTimeout')}, 0)async function async1() {console.log('async1 start')await async2();console.log('async1 end')}async function async2() {console.log('async2')}console.log('script start')async1();console.log('script end')
運(yùn)行結(jié)果:

是不是恍然大明白呢(●'?'●)。
推薦閱讀:
異步無處不在:Promise 破除“回調(diào)地獄”(三)
技術(shù)人年度總結(jié) | 2020,注定不平凡
前端人因為 Vue3 的 Ref-sugar 提案打起來了!
點點“贊”和“在看”,保護(hù)頭發(fā),減少bug。
