【问题标题】:How can I invoke a callback given to a yield in javascript v8 generator code?如何在 javascript v8 生成器代码中调用给定收益的回调?
【发布时间】:2014-06-17 13:22:54
【问题描述】:

我正在尝试理解 node.js v8 中的 javascript 生成器,不使用任何第三方库。

我想尝试使用一个生成器来调用异步回调(类似于 Web 请求)并返回被回调的值。

我的代码(使用 Node v0.11.12)如下所示:

var callbackWrapper = function (){
  return function(callback) {
    callback(null, 5);
  };
};

var result = null;
var generator = (function *(){
  result = yield callbackWrapper();
})();

var nextIteration = generator.next();

在这段代码的最后,nextIteration.value是一个函数,result仍然为null。

我的理解是,如果给 yield 一个带有回调的方法,它会调用该回调。

我的目标是以某种方式获得被回调的值 5。

这样做的方法是什么?我是否必须将一些函数传递给 nextIteration.value 并调用它?

【问题讨论】:

  • “我的理解是,如果给 yield 一个带有回调的方法,它会调用该回调。”没有。yield 只是返回一个值给 generator.next() 的调用者

标签: javascript node.js generator ecmascript-harmony


【解决方案1】:

让我们澄清一些事情。

我的理解是,如果给 yield 一个带有回调的方法,它会调用该回调。

yield 不调用任何东西。 yield 的目的是产生......当你定义一个生成器时,你定义了一个产生值的实体。它与异步代码无关。但是,我们可以利用生成器的一个重要属性来处理异步代码。

根据其定义,生成器产生值。 这是重要的部分 - 在一个产量到另一个产量之间,生成器暂停。换句话说,它一直等到被要求产生下一个值。生成器的内部状态保存在内存中(在堆栈上),直到生成器耗尽(没有更多的值可以产生)。

生成器的另一个重要特性是我们可以将值发回,从而改变它们的内部状态。

因此,如果我们可以暂停生成器(让它们等待)并且我们可以将值发送回来;我们基本上可以让他们等待一些异步操作完成,然后将结果发回。

这样做的方法是什么?我是否必须将一些函数传递给 nodeIteration.value 并调用它?

基本上,您需要使用生成器来包装您的代码。每次启动异步操作时,都会使用yield 让生成器等待。异步操作完成后,您可以通过将结果发送回 next(result) 来恢复生成器。

我写了一篇详尽的文章来解释这个问题,我相信它会帮助你理解。看一看:http://eyalarubas.com/javascript-generators-and-callbacks.html

【讨论】:

  • EyalAr 感谢您的博客文章 - 我正在寻找一个解释生成器本身的指南,而不是谈论使用它们的库。您只需将分享按钮添加到您的博客!
【解决方案2】:

生成器不会自行处理节点样式回调。相反,它返回的是您包装在 callbackWrapper thunk 中的函数。因为 yield 只返回一个值,然后在那个时间点暂停执行。

生成器并不是真正为控制流设计的,但您可以在它们之上构建控制流库,如 co、suspend 等。

基本上这些库的作用(我在这里过于简单化了)就是获取您的生成器函数并递归调用它,直到它告诉它们它已经完成。

这些库中的每一个都以不同的方式处理内部产量,例如,共同将它可以处理的所有内容都转换为内部的 thunk。而suspend在内部使用节点样式的回调。

在每次 yield 时,他们会检查向他们屈服的 thunk、promise、generator 或库处理的任何构造,并根据它们何时完成抽象出控制。

您可以围绕生成器构建一个结构来处理异步 thunked 函数,但这不适合虚心。基本上它会像这样(注意:不要使用它,因为它缺少所有正常检查、错误处理等。):

function controlFlow(genFunc){
  // check to make sure we have a generator function otherwise explode

  var generator; // reference for an initialized generator

  // return a funcion so that execution time can be postponed
  return function(){
    runGen(genFunc());
  }

  function runGen(generator){
    var ret = generator.next();

    // here the generator is already finished we we'll return the final value
    if(ret.done){
      return ret.value
    }

    // here we'll handle the yielded value no matter what type it is
    // I'm being naive here for examples sake don't do this
    if(typeof ret.value === 'function'){
      // oh look we have a regular function (very faulty logic)
      // we wouldn't do this either but yeah
      ret.value(function(err, result){
        console.log(result);
      });
    }

   // oh we got another item like an array or object that means parallelism or serialization depending on what we got back
   // turn array, object, whatever into callback mechanisms and then track their completion
   // we're just going to fake it here and just handle the next call
   runGen(generator);
  }
}

function thunked(callback){
  return function(){
    callback(null, 5);
  };
};

function regular(callback){
  console.log('regular function call');
  callback(null, 'value');
};

controlFlow(function *(){
  yield thunked(function(err, result){
    console.log(err);
    console.log(result);
  });
  yield regular;
  yield thunked(function(err, result){
    console.log('Another Thunked');
  });
  yield regular(function(err, result){
    console.log(err);
    console.log(result);
  });
})();

【讨论】:

  • 实际上生成器为控制流而设计的。您的意思是,它们并非专门用于处理异步回调。
  • 生成器最初是为了更简单地构建复杂的迭代器,所以是的,它们是用于控制流的。我试图在解释中过于简单,
  • 我非常喜欢语言结构的强大功能和灵活性,但如果没有使用另一种语言,人们似乎很难掌握它们......也许是时候了从其他语言中挖掘一些示例并将它们转换为单一资源?
【解决方案3】:

result 不会被分配,直到您再次调用 next 并使用您要分配给 result 的值将值发送回生成器。在您的示例中,如下所示:

nextIteration.value(function (error, value) {
  generator.next(value);
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-24
    • 2011-05-04
    • 2013-07-03
    • 2012-11-29
    • 1970-01-01
    • 2012-04-28
    • 2016-08-25
    • 2013-05-21
    相关资源
    最近更新 更多