我们如何使用生成器以同步方式编写异步代码?

时间:2014-02-09 19:28:47

标签: javascript node.js ecmascript-harmony

我已经读过,ECMAScript 6附带并且已经在node.js开发版中提供的生成器将更容易以同步方式编写异步代码。但是我真的很难理解,我们如何使用生成器编写异步代码?

2 个答案:

答案 0 :(得分: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);
}

发电机示例:

如果要将它与Promises一起使用,我们必须使用一个控制流的库,如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);
}

我们做什么:

  • 创建生成器,给出一个resume函数作为第一个参数,一个由函数caller
  • 给出的参数
  • 第一次致电next()。达到了我们的第一个异步功能和发电机产量。
  • 每次调用resume函数时,我们得到值并将其传递给next,因此生成器中的代码继续执行,yield语句返回正确的值。

在现实生活中,我们将使用库,并将生成器作为参数提供给通用控制器函数。因此,我们必须编写的是生成器,如果使用Promises(与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

答案 1 :(得分:0)

补充Guilro的答案,生成器允许你写这样的东西:

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

控制器函数执行的操作是调用生成器,获取从yield返回的任何内容,将next()调用链接到返回值的then(),直到生成器为done。< / p>

这样的事情:

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);

}