我试图在不使用任何第三方库的情况下理解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 并调用它?
答案 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);
});