3

While I was trying to use 'koa-router' module for koa, I saw the example code snippet below.

app.get('/users/:id', function *(next) {
  var user = yield User.findOne(this.params.id);
  this.body = user;
});

My question is, why does it have yield right before it gets user info? Why can't code be something like below without yield? Is there major difference?

app.get('/users/:id', function *(next) {
  var user = User.findOne(this.params.id);
  this.body = user;
});
4

4 回答 4

3

带有星号的函数function *(){}是一个生成器函数,它允许通过使用关键字在函数内暂停恢复流。yield

没有 yield 的生成器函数是没有用的,它们是齐头并进的。

在koa的幕后,您的生成器函数被co库调用,该库处理所有异步操作,将回调/承诺抽象到库中,为您留下更简单的代码。

我创建了一个关于理解生成器的截屏视频,您可能会觉得它有帮助。

于 2014-12-23T23:27:02.413 回答
1

Because the body would be set to a value which won't exist until the future.

You can think of the yield here as a kind of wait keyword, where the waiting behavior is being handled by the Koa infrastructure. You yield a promise, then Koa waits for the promise to fulfill, then it calls your generator again (well, actually, calls next() on the iterator based on the generator), using the promise's resolved value as the argument to next, which means it gets assigned to the variable on the left-hand side of the yield.

So in this case, you make the DB call, yield the resulting promise to Koa, Koa waits for it to fulfill, then passes back the fulfilled value which is assigned to the variable user, and the function runs either until the next yield, or until it falls off the bottom.

It might be clearer if you consider how you would handle two asynchronous tasks in the same request:

app.get('/users/:id', function *(next) {
    var user = yield User.findOne(this.params.id);
    var data = yield someOtherAsynchronousTaskReturningProimse(user);
    this.body = data;
});

In this case, you are "jumping up and down on the trampoline" twice.

Depending on how you choose to think of this, you can sorta kinda treat this as the more readable equivalent of something like

function handle(id) {
    User.findOne(id)
        .then(function(user) {
            someOtherAsynchronousTaskReturningPromise(user)
                .then(function(data) {
                    setDataIntoResponse(data);
                });
        });
}

I think you'll agree that the so-called "co-routine" based approach is much more readable. Another advantage of this architecture, besides allowing you to write your middleware/handlers in yield...yield fashion, is that Koa can go about doing other useful things while it's waiting for the promise you returned to resolve.

于 2014-12-24T03:53:29.533 回答
1

我不知道科亚。但它似乎使用了和声发生器(可从节点 0.11 获得)。

本质上,第一部分与以下内容相同:

app.get('/users/:id', function *(next) {
  var self = this;
  User.findOne(this.params.id).then(function(user) {
    self.body = user;
  });
});

当 yield 接受的 promise 被解决时,yield 将返回已实现的值,并且程序将从那里恢复。is(据function *(..)我所知)也是一个允许使用 yield 的特殊结构。

于 2014-12-23T03:09:00.117 回答
0

KOA 使用生成器函数来简化所谓的中间件的注入。来自 KOA 的网站 ( http://koajs.com/ ):

当中间件调用 yield next 时,函数会挂起并将控制权传递给定义的下一个中间件。在没有更多的中间件在下游执行之后,堆栈将展开,每个中间件都恢复执行其上游行为。

例子:

var app = Koa();

app.use (function* (next) {
    this.body = '{';
    yield next;
    this.body += '}';
});

app.use (function* () {
    this.body += 'Hello World!';
});

该网络服务器将响应字符串“{Hello World!}”。为什么?执行流入第一个生成器函数,将 body 设置为 '{' 并在 处暂停yield next。继续执行下一个生成器函数并附加“Hello World!”。该函数结束,因此执行流回到第一个暂停的地方,它附加了'}'。

但是,在您的情况下,它有点复杂,因为 yield 用于传回除next. 例如,在 KOA 中,您还可以产生承诺。看看这个(我在这里找到:http: //blog.stevensanderson.com/2013/12/21/experiments-with-koa-and-javascript-generators/

app.use(function *() {
    var response = yield doHttpRequest('http://example.com/');
    this.body = "Response length is " + response.body.length;
});

在此示例中,yield从异步 Http 请求中传回 promise(由辅助函数 doHttpRequest 完成,此处未显示)。KOA 会暂停函数,直到 promise 完成,然后执行“this.body=...”继续。在您的代码段中, User.findOne() 可能会返回这样的承诺。(不一定;KOA 允许您传回其他类型的对象,请参阅文档。)

于 2015-08-13T13:04:15.147 回答