異步編程系列教程:
-
(翻譯)異步編程之Promise(1)——初見魅力
-
異步編程之Promise(2):探究原理
-
異步編程之Promise(3):拓展進(jìn)階
-
異步編程之Generator(1)——領(lǐng)略魅力
-
異步編程之Generator(2)——剖析特性
- 異步編程之co——源碼分析
為何使用Generator
回顧一下我們之前學(xué)習(xí)的promise。我們巧妙利用了
promise/deferred
模式,用鏈?zhǔn)浇Y(jié)構(gòu)代替了嵌套回調(diào)的結(jié)構(gòu),大大緩解了回調(diào)地獄。我們再來看看之前我們舉的那個異步串行隊(duì)列的例子吧!假設(shè)我們有一個
hello.txt
,里面存了一個JSON文件的文件名,我們需要得到JSON文件的message屬性的值。步驟如下:
-
讀取
hello.txt
文件
-
得到JSON文件名,再次讀取文件
-
得到JSON數(shù)據(jù)后,進(jìn)行JSON解析
- 獲得JSON的message屬性
Promise鏈?zhǔn)秸{(diào)用
這個例子我們之前也是舉過非常多次的,我們嘗試使用Promise鏈?zhǔn)浇Y(jié)構(gòu)完成:
//這里的readFile已經(jīng)是promise化的異步API
readFile('hello.txt', 'utf-8')
.then(function(filename){
return readFile(filename, 'utf-8');
})
.then(JSON.parse)
.then(function(data){
console.log(data.message);
})
.catch(function(err){
console.error(err.message);
});
這樣一看下來,promise好像并沒有多大問題,思維是線性的,而且錯誤處理也很友好。我們只需要把上一層執(zhí)行后的結(jié)果通過
then()
傳到下一步執(zhí)行即可。嗯,但不得不說被鏈?zhǔn)浇Y(jié)構(gòu)束縛后,我們并沒有得到一種酣暢淋漓的編程體驗(yàn)。
同步API
我們要寫的爽,當(dāng)然是要將異步編程得到同步編程的體驗(yàn),這樣我們直接使用同步API看一下是怎樣的:
var filename = fs.readFileSync('hello.txt', 'utf-8');
var json = fs.readFileSync(filename, 'utf-8');
console.log(JSON.parse(json).message);
同步的寫法清晰明了,而且更符合我們以往的編程習(xí)慣。但是同步API阻塞代碼這個弊病會在Javascript的單線程執(zhí)行中非常明顯。我們到底有沒有一種既可以非常接近同步編程的寫法,又可以異步不阻塞代碼執(zhí)行呢?既然問出這種問題,答案當(dāng)然是有的,就是今天的主角: Generator 。
Generator使用co寫法
Generator,顧名思義是一個構(gòu)造器,它本身是用來生成迭代器的。它是ES6的新東西,所以你為了使用它,需要在node中開啟harmony模式才能體驗(yàn)到它。
$ node --harmony
基于Generator,TJ大神做了一個
co
庫。
co
在最新的版本里,結(jié)合Generator和Promise改善了異步編程的體驗(yàn),也就是我們之前說的:
既可以同步,又不會阻塞
。
還是一樣的例子,我們結(jié)合promise的代碼和同步API的代碼對比看看:
co(function* (){
var filename = yield readFile('hello.txt', 'utf-8');
var json = yield readFile(filename, 'utf-8');
return JSON.parse(json).message;
}).then(console.log, console.error);
非常像有沒有,我們不再需要將每一次異步的結(jié)果都放在
then()
中進(jìn)行處理,我們可以通過類似于同步的寫法調(diào)用Promise異步API,大大提升編程體驗(yàn)。最后
co()
返回了一個promise對象,提供我們做最后的數(shù)據(jù)處理和錯誤處理。我們從同步API轉(zhuǎn)到
co
,僅僅需要做到以下幾點(diǎn):
-
co里面?zhèn)鞯暮瘮?shù)標(biāo)識符需要加上*號,
function*
。這也就是Generator函數(shù)
-
調(diào)用promise異步API之前,都要加上
yield
標(biāo)識符
-
將需要做最后處理的數(shù)據(jù)
return
出來,在then()
中進(jìn)行處理即可
預(yù)習(xí)Generator
我們在舉完異步串行的例子后,這次的文章就接近尾聲了。最后我們可以大致了解一下
co
到底是如何運(yùn)作的呢?我們會在接下來的文章進(jìn)行深究,這一次就簡單說一說,你當(dāng)作預(yù)習(xí)就可以了:
Generator相關(guān)
-
Generator生成迭代器后,等待迭代器的
next()
指令啟動。
-
啟動迭代器后,代碼會運(yùn)行到y(tǒng)ield處停止。并返回一個
{value: AnyType, done: Boolean}
對象,value
是這次執(zhí)行的結(jié)果,done
是迭代是否結(jié)束。并等待下一次的next()
指令。
-
next()
再次啟動后。若done
屬性不為true,則可以繼續(xù)從上一次停止的地方繼續(xù)迭代。
-
一直重復(fù)2,3步驟,直到
done
為true。
co相關(guān)
-
co內(nèi)部的迭代器對象是被封裝成Promise的。
-
yield
后面跟的必須是一個promise化的異步API,所以next()
得到的結(jié)果是一個promise對象。
-
若迭代沒有結(jié)束,則
co
會自動為該異步promise對象的resolve
中,增添一個next()
。通過前面的異步執(zhí)行完回調(diào)后,再調(diào)用next()
,使迭代器的代碼不斷向前執(zhí)行。
-
若迭代結(jié)束,則直接調(diào)用整個迭代器對象的
resolve
。
總結(jié)
或許現(xiàn)在大家看的是一知半解,或許很興奮想知道更多相關(guān)的。若僅僅是想學(xué)會用co,我想上面的大概已經(jīng)足夠你看了。但是想更深入,你必須先弄懂promise的原理和Generator的相關(guān)特性。最后使用
co
庫一定會得心應(yīng)手。
接下來,我會先講一些關(guān)于Generator的相關(guān)特性,再配合之前說過的promise,深入到
co
的源碼學(xué)習(xí)中。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
