2014-02-09 61 views
0

我已經讀過ECMAScript 6附帶的並且已經在node.js dev版本中可用的生成器,可以更輕鬆地以同步方式編寫異步代碼。但是我很難理解,我們如何使用生成器來編寫異步代碼?我們如何使用生成器以同步方式編寫異步代碼?

+0

那麼,爲什麼你要問,如果你已經知道答案嗎? – vkurchatkin

+2

http://stackoverflow.com/help/self-answer – Guilro

+1

[嘗試理解node.js中的生成器/ yield - 執行異步函數的原因?](http://stackoverflow.com/questions/17516952/試圖理解生成器 - 在節點中生成yield-in-js-what-executed-the-asynchron) – vkurchatkin

回答

2

我們首先必須記住,使用ES生成器,我們可以將值傳遞給next()方法,該方法將成爲生成器中yield語句的返回值。

的想法是給發電機的一種控制器的功能。

發電機,每一個我們稱爲異步函數的時候,我們yield,所以我們回饋控制,控制器功能。當異步操作完成時,控制器功能只會調用next()。在此期間,我們可以處理其他事件,因此它是非阻塞的。

例無發電機:

// chain of callbacks 
function findAuthorOfArticleOfComment (commentID, callback) { 
    database.comments.find({id: commentID} 
         , function (err, comment) { 
    if (err) return callback(err); 
    database.articles.find({ id: comment.articleID } 
          , function (err, article) { 
     if (err) return callback(err); 
     database.users.find({ id: article.authorID } 
         , function (err, author) { 
     if (err) return callback(err); 
     callback(author); 
     }); 
    }); 
    }); 
} 
findAuthorOfArticleOfComment (commentID, function(err, author) { 
    if(!err) console.log(author); 
} 

例如用發電機:

我們必須使用,如果你想使用它,這將控制流量,像suspendbluebird庫承諾。爲了更好的理解,我將舉一個沒有圖書館的例子。

function* myGenerator(resume, commentID, callback) { 
    var comment, article, author; 
    comment = yield database.comments.find({id: commentID}, resume); 
    article = yield database.articles.find({id: comment.articleID}, resume); 
    author = yield database.users.find({id: article.authorID}, resume); 
}; 

// in real life we use a library for this ! 
var findAuthorOfArticleOfComment = function(commentID, callback) { 
    var resume, theGenerator; 
    resume = function (err, result) { 
    var next; 
    if(err) return callback(err); 
    next = theGenerator.next(result); 
    if (next.done) callback(null, result); 
    } 
    theGenerator = myGenerator(resume, commentID, callback); 
    theGenerator.next(); 
} 

// still the same function as first example ! 
findAuthorOfArticleOfComment (commentID, function(err, author) { 
    if(!err) console.log(author); 
} 

我們做什麼:

  • 創建發電機,給人一種恢復功能作爲第一個參數,通過函數調用
  • 呼叫next()第一次給出的參數。我們的第一個異步功能已經達到並且發電機產量。每次resume函數被調用
  • ,我們得到的值,並把它傳遞給下一個,所以在生成代碼繼續執行和產量的語句返回正確的值。

在現實生活中,我們將使用庫,並將生成器作爲參數傳遞給通用控制器函數。所以我們必須寫的是生成器,如果使用Promise(帶有Promise兼容庫),則可以使用value = yield asyncFunction(parameters, resumeCallback);value = yield functionReturningPromise(parameters);。以同步的方式編寫異步代碼是一種非常好的方法。


最佳來源:
http://tobyho.com/2013/06/16/what-are-generators/
http://jlongster.com/A-Closer-Look-at-Generators-Without-Promises

+0

你爲什麼要用發電機的'resume'回調?它看起來有點醜陋。我們不能像同步代碼一樣返回嗎? – Bergi

+0

這個例子儘可能簡單地理解它如何在沒有魔法的情況下工作。大多數時候你會使用藍鳥這樣的庫,它支持Promises。在這種情況下,你確實會產生一個承諾,藍鳥會在承諾解決時調用next()。但是如果你只是在沒有使用Promises的情況下返回,這將是同步並因此阻止代碼,它將失去所有興趣。本身的收益不會增加任何異步魔法,它只會停止生成器的執行。所以如果你想要更多的「魔法」,並使用回報而不是回叫,你最好檢查藍鳥。 – Guilro

+0

我知道(我使用的承諾的話),我只是認爲有可能是一個更好的模式來得到結果出來'myGenerator'的(不出來異步'findAuthorOfArticleOfComment',當然) – Bergi

0

補全Guilro的回答,發生器可以這樣寫:

controllerFunction(function*() { 
    yield functionThatReturnsAThenable() 
    yield anotherFunctionThatReturnsAThenable() 
    return; 
}); 

控制器功能所做的是調用發電機,從收益中獲得任何回報,將next()撥打then()直到發電機爲done

事情是這樣的:

function controllerFunction(generator) { 

    function run(runnableGenerator) { 
     var result = runnableGenerator.next(); // or send(params) 
     if (!result.done) { 
      if (result.value.then) { 
       result.value.then(function() { 
        run(runnableGenerator); 
       }); 
      } else { 
       run(runnableGenerator); 
      } 
     } 
    } 

    var runnableGenerator = generator(); 
    run(runnableGenerator); 

}