KOA中的发电机

时间:2015-05-12 17:53:32

标签: javascript node.js koa

如何在KOA中使用app.use? 当我在app.use中设置一些生成器时,一切都很完美。 我怎么能在其他地方做同样的事情?

当我执行发电机手册时:

var getRelationsList = function *() {
  var res = yield db.relations.find({});
  console.log({'inside: ': res});
}
console.log({'outside: ': getRelationsList().next()});
getRelationsList().next();

我只是{'外面:':{值:[功能],完成:假}}

我的期望是:

{ 'outside: ': { value: {object_with_results}, done: false } }
{ 'inside: ': {object_with_results}

修改

我改变了我的代码:

var getRelationsList = function *() {
  var res = yield db.relations.find({});
  console.log({'inside: ': res});
}
console.log({'outside ': co(getRelationsList)});

现在在控制台日志中显示我的结果很好但是在控制台日志之外只显示空对象。 enter image description here

3 个答案:

答案 0 :(得分:4)

生成器是组织异步代码的强大工具,但它们不会神奇地等待异步代码运行。

发生了什么

让我们一起浏览您的代码,以便了解正在发生的事情:

getRelationsList是一个生成器函数,在调用时会返回一个新生成器。此时,您的生成器函数中没有调用任何代码(尽管如果您正在传递params,则会设置它们)。然后,在生成器上调用.next以开始执行生成器功能。它将执行直到它到达第一个yield语句并返回一个具有生成值和生成器完成状态的对象。

到目前为止你似乎已经理解了大部分内容,但是生成器并没有神奇地改变所产生的值。当你产生db.relations.find({})时,你将得到find函数的返回值,我假设它是一个Promise或者某种类型的函数:

因此您的“外部”值为{ value:Promise, done:false }

您的内部console.log从未投放的原因是您每次拨打getRelationsList()时实际上都在创建新的生成器,因此当您在外部{{1}之后再次呼叫getRelationsList().next()时你正在创建一个新的生成器并调用next,所以它只执行第一个yield,就像上一行的调用一样。

为了完成执行,你必须在你的生成器的同一个实例上调用next两次:一次执行到yield,一次继续执行到函数的结尾。

console.log

但是,您会注意到,如果您运行此功能,内部var gen = getRelationsList() gen.next() // { value:Promise, done:false } gen.next() // { value:undefined, done:true } (will also console.log inside) 将为console.log。这是因为undefined语句的值等于传递给后续yield调用的值。

例如:

.next()

输出

var gen2 = getRelationsList()
gen2.next() // { value:Promise, done:false }
gen2.next(100) // { value:undefined, done:true } 

因为我们将第100个调用传递给了第二个{ inside:100 } ,这就成了.next()语句的值,然后将其分配给yield db.relations.find({})

这是一个演示所有这些内容的链接:http://jsfiddle.net/qj1aszub/2/

解决方案

koa的创建者使用一个名为co的小库,它基本上取出了promises并等待它们完成,然后将解析后的值传回生成器函数(使用res函数),这样您可以用同步样式编写异步代码。

co将返回一个promise,这将要求您调用.next()方法以获取从生成器函数返回的值。

.then

co还允许你输出其他生成器函数并等待它们完成,所以你不必在每个级别用co包装东西并处理promises,直到你处于某种“顶层”:

var co = require('co');
var getRelationsList = function *() {
    var res = yield db.relations.find({});
    console.log({'inside: ': res});
    return res
}

co(getRelationsList).then(function(res) {
    console.log({'outside: ': res })
}).catch(function(err){
    console.log('something went wrong')
});

答案 1 :(得分:1)

生成器必须由外部代码执行。 在引擎盖koa下使用co库来“运行”生成器。

以下是你如何在koa之外实现你想要的东西:

var co = require('co');
var getRelationsList = function *() {
  var res = yield db.relations.find({});
  console.log({'inside: ': res});
}

co(getRelationsList).catch(function(err){});

我对JavaScript生成器做了一个简短的截屏视频,可以帮助您了解正在发生的事情:

http://knowthen.com/episode-2-understanding-javascript-generators/

++编辑

如果您使用生成器以更多同步方式编程(消除回调),那么您需要在生成器中完成所有工作,并且应该使用像co这样的库来执行生成器。

以下是有关如何手动与生成器交互的更详细示例。这可以帮助您了解获得的结果。

function * myGenerator () {
  var a = yield 'some value';
  return a;
}
var iterator = myGenerator();
// above line just instantiates the generator
console.log(iterator);
// empty object returned
// {}

var res1 = iterator.next();
// calling next() start the generator to either the 
// first yield statement or to return.
console.log(res1);
// res1 is an object with 2 attributes 
// { value: 'some value', done: false }
// value is whatever value was to the right of the first 
// yield statment 
// done is an indication that the generator hasn't run
// to completion... ie there is more to do
var toReturn = 'Yield returned: ' + res1.value;
var res2 = iterator.next(toReturn);
// calling next(toReturn) passes the value of 
// the variable toReturn as the return of the yield 
// so it's returned to the variable a in the generator 
console.log(res2);
// res2 is an object with 2 attributes 
// { value: 'Yield returned: some value', done: true }
// no further yield statements so the 'value' is whatever
// is returned by the generator.
// since the generator was run to completion 
// done is returned as true

答案 2 :(得分:1)

您的问题是您多次调用getRelationsList()函数,这是不正确的。

将您的代码更改为以下

var g = getRelationsList();
console.log('outside: ', g.next());
g.next(); //You get your console.log('inside: .... here
相关问题