Why is it necessary to have an "exit" in this statement from a node application?

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

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 getting user info? Why can't the code be something like below without a tutorial? Is there a significant difference?

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

      

+3


source to share


4 answers


The asterisk function *(){}

function is a generator function, which allows you to pause and resume a thread inside a function using yield

.

The function of a generator without an output is useless, they go hand in hand.



Behind the scenes, the koa generator function is called by co , which handles all the asynchronous callback / prom abstraction in the library, leaving you with simpler, simpler code.

I created a screencast on Understanding Generators that might help you.

+3


source


I don't know koa. But it seems to be using 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 that yields income is resolved, the yield will return the fulfilled value and the program will resume from there. function *(..)

(to my knowledge) is also a special construct that allows the output to be used.

+1


source


Because the body will be set to a value that won't exist until the future.

You can think of yield

here as a kind of keyword wait

where the wait behavior is handled by the Koa framework. You make a promise, then Koa waits for the promise to be fulfilled, then he calls your generator again (well, actually, calls next()

on a generator based iterator) using the value of the promise as an argument next

, which means it is assigned to the variable on the left yield

.

So, in this case, you call the DB call, give the received promise to Koa, Koa waits for it to be fulfilled, and then passes back the fulfilled value, which is assigned to the variable user

, and the function runs until either the next exit or until it falls from the bottom ...

It might be better if you looked at how you would handle two asynchronous tasks in one 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 "jump up and down the trampoline" twice.

Depending on how you decide to think about it, you can sort it as a more readable equivalent to something like

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

      

I think you will agree that the so-called collaborative approach is much more readable. Another advantage of this architecture, besides the fact that you can write your middleware / handlers in style yield...yield

, is that Koa can do other useful things while waiting for the promise you made.

+1


source


KOA uses generator functions to facilitate the injection of so-called mid-season. From the KOA website ( http://koajs.com/ ):

When the middleware calls the next exit, the function pauses and passes control of the next middleware. After there are no more downstream middleware, the stack disintegrates and each middleware resumes to perform its upstream actions.

Example:

var app = Koa();

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

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

      

This web server will respond with the string '{Hello World!}'. What for? Execution goes to the first generator function, sets the body to '{', and pauses at yield next

. Execution continues with the next generator function and adds "Hello World!" This function ends, so execution returns to where the first one was suspended, which adds "}".

In your case, however, this is a bit tricky since the output is being used to communicate something other than next

. For example, in KOA, you can get promises as well. Take 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;
});

      

This example yield

returns a promise from an asynchronous Http request (performed by the doHttpRequest helper function, not shown here). KOA stops this function until the promise is fulfilled, then execution continues with "this.body = ...". In your snippet, User.findOne () is probably returning a promise like this. (Optional, KOA allows other objects to be passed, see docs.)

0


source







All Articles