user482594
user482594

Reputation: 17486

Why is it necessary to have 'yield' in this statement from node app?

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;
});

Upvotes: 3

Views: 1077

Answers (4)

digory doo
digory doo

Reputation: 2311

KOA uses generator functions to simplify the injection of so-called middlewares. From KOA's website (http://koajs.com/):

When a middleware invokes yield next the function suspends and passes control to the next middleware defined. After there are no more middleware to execute downstream, the stack will unwind and each middleware is resumed to perform its upstream behaviour.

Example:

var app = Koa();

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

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

This webserver will respond with the string '{Hello World!}'. Why? Execution flows into the first generator function, sets body to '{' and pauses at yield next. Execution continues with the next generator function and appends 'Hello World!'. That function ends, so execution flows back to where the first one paused, which appends '}'.

In your case however, it's a bit more complicated insofar that yield is used to pass back something other than next. In KOA, you may also yield promises, for example. Have a look at this (which I found here: 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;
});

In this example, yield passes back the promise from an asynchronous Http request (done by the helper function doHttpRequest, which is not shown here). KOA pauses the function until the promise is fulfilled, then execution continues with "this.body=...". In your snippet, User.findOne() probably returns such a promise. (Not necessarily; KOA allows you to pass back other kinds of objects, see the docs.)

Upvotes: 0

user663031
user663031

Reputation:

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.

Upvotes: 1

James Moore
James Moore

Reputation: 1901

The function with the asterisk function *(){} is a generator function, which allows pausing and resuming flow within the function by using the yield keyword.

A generator function without a yield is useless, they go hand in hand.

Behind the scenes in koa your generator function is being called by the co library which handles all the asynchronous operations, abstracting the callback / promise into the library, leaving you with a flatter simpler code.

I created a screencast on understanding generators that you might find helpful.

Upvotes: 3

tokosh
tokosh

Reputation: 1836

I don't know koa. But it seems to make use of harmony generators (available from node 0.11).

Essentially the first part is the same as:

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

When the promise the yield takes is resolved, the yield will return the fulfilled value and the program will be resumed from there. The function *(..) is (as far as I know) also a special construct that allows the use of yield.

Upvotes: 1

Related Questions