異步編程之Generator(1)——領略魅力

異步編程系列教程:node

  1. (翻譯)異步編程之Promise(1)——初見魅力
  2. 異步編程之Promise(2):探究原理
  3. 異步編程之Promise(3):拓展進階
  4. 異步編程之Generator(1)——領略魅力
  5. 異步編程之Generator(2)——剖析特性
  6. 異步編程之co——源碼分析

爲什麼使用Generator


回顧一下咱們以前學習的promise。咱們巧妙利用了promise/deferred模式,用鏈式結構代替了嵌套回調的結構,大大緩解了回調地獄。咱們再來看看以前咱們舉的那個異步串行隊列的例子吧!假設咱們有一個hello.txt,裏面存了一個JSON文件的文件名,咱們須要獲得JSON文件的message屬性的值。步驟以下:git

  1. 讀取hello.txt文件
  2. 獲得JSON文件名,再次讀取文件
  3. 獲得JSON數據後,進行JSON解析
  4. 得到JSON的message屬性

Promise鏈式調用

這個例子咱們以前也是舉過很是屢次的,咱們嘗試使用Promise鏈式結構完成:github

//這裏的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好像並無多大問題,思惟是線性的,並且錯誤處理也很友好。咱們只須要把上一層執行後的結果經過then()傳到下一步執行便可。嗯,但不得不說被鏈式結構束縛後,咱們並無獲得一種酣暢淋漓的編程體驗。編程

同步API

咱們要寫的爽,固然是要將異步編程獲得同步編程的體驗,這樣咱們直接使用同步API看一下是怎樣的:json

var filename = fs.readFileSync('hello.txt', 'utf-8');
var json = fs.readFileSync(filename, 'utf-8');
console.log(JSON.parse(json).message);

同步的寫法清晰明瞭,並且更符合咱們以往的編程習慣。可是同步API阻塞代碼這個弊病會在Javascript的單線程執行中很是明顯。咱們到底有沒有一種既能夠很是接近同步編程的寫法,又能夠異步不阻塞代碼執行呢?既然問出這種問題,答案固然是有的,就是今天的主角:Generatorapi

Generator使用co寫法

Generator,顧名思義是一個構造器,它自己是用來生成迭代器的。它是ES6的新東西,因此你爲了使用它,須要在node中開啓harmony模式才能體驗到它。promise

$ 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對象,提供咱們作最後的數據處理和錯誤處理。咱們從同步API轉到co,僅僅須要作到如下幾點:

  • co裏面傳的函數標識符須要加上*號,function*。這也就是Generator函數
  • 調用promise異步API以前,都要加上yield標識符
  • 將須要作最後處理的數據return出來,在then()中進行處理便可

預習Generator


咱們在舉完異步串行的例子後,此次的文章就接近尾聲了。最後咱們能夠大體瞭解一下co究竟是如何運做的呢?咱們會在接下來的文章進行深究,這一次就簡單說一說,你看成預習就能夠了:

Generator相關

  1. Generator生成迭代器後,等待迭代器的next()指令啓動。
  2. 啓動迭代器後,代碼會運行到yield處中止。並返回一個{value: AnyType, done: Boolean}對象,value是此次執行的結果,done是迭代是否結束。並等待下一次的next()指令。
  3. next()再次啓動後。若done屬性不爲true,則能夠繼續從上一次中止的地方繼續迭代。
  4. 一直重複2,3步驟,直到done爲true。

co相關

  1. co內部的迭代器對象是被封裝成Promise的。
  2. yield後面跟的必須是一個promise化的異步API,因此next()獲得的結果是一個promise對象。
  3. 若迭代沒有結束,則co會自動爲該異步promise對象的resolve中,增添一個next()。經過前面的異步執行完回調後,再調用next(),使迭代器的代碼不斷向前執行。
  4. 若迭代結束,則直接調用整個迭代器對象的resolve

總結

或許如今你們看的是隻知其一;不知其二,或許很興奮想知道更多相關的。若僅僅是想學會用co,我想上面的大概已經足夠你看了。可是想更深刻,你必須先弄懂promise的原理和Generator的相關特性。最後使用co庫必定會駕輕就熟。

接下來,我會先講一些關於Generator的相關特性,再配合以前說過的promise,深刻到co的源碼學習中。

相關文章
相關標籤/搜索