如何在javascript v8生成器代码中调用赋予yield的回调?

时间:2014-05-01 02:55:37

标签: javascript node.js generator ecmascript-harmony

我试图在不使用任何第三方库的情况下理解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 是一个函数,结果仍为空。

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

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

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

3 个答案:

答案 0 :(得分:4)

让我们澄清一些事情。

  

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

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

根据他们的定义,发电机产生价值。 这是重要的部分 - 在一个产量与另一个产量之间,生成器暂停。换句话说,它等待它被要求产生下一个值。发生器的内部状态保存在存储器中(在堆栈上),直到发生器耗尽(不再产生值)。

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

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

  

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

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

我写了一篇广泛的帖子来解释这个问题,我相信这一点可以帮助你理解。看看:http://eyalarubas.com/javascript-generators-and-callbacks.html

答案 1 :(得分:3)

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

生成器并非真正设计用于控制流,但您可以在它们之上构建以创建控制流库,如co,suspend等。

基本上这些库所做的事情(我在这里过于简化)是采用你的生成器函数并递归调用它,直到它告诉它已经完成。

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

在每次收益时,他们会检查是什么产生了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);
  });
})();

答案 2 :(得分:0)

只有在您使用要分配给result的值再次调用next后,才会将值发回给生成器,才会分配

result。在您的示例中,如下所示:

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