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

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