JS Generators: How is `return yield` different from `yield`?

First, I’ll start by saying that Generators are a somewhat complicated topic, so giving a complete overview here won’t be possible. For more information I’d highly recommend Kyle Simpson’s You Don’t Know JS series. Book 5 (Async & Performance) has an excellent discussion on the ins and outs of generators.

Onto the specific example you gave though!

First, the code that you wrote in the example will show no difference but only if it’s run correctly. Here’s an example:

function* foo() {
  yield 123;
}

function* bar() {
  return yield 123;
}

var f = foo();
var b = bar();

f.next(); // {value: 123, done: false}
f.next(); // {value: undefined, done: true}
b.next(); // {value: 123, done: false}
b.next(); // {value: undefined, done: true}

As you can see, I’m not calling the generator like a normal function. The generator itself returns a generator object (a form of iterator). We store that iterator in a variable and use the .next() function to advance the iterator to the next step (a yield or return keyword).

The yield keyword allows us to pass a value into the generator, and this is where your examples will run differently. Here’s what that would look like:

function* foo() {
  yield 123;
}

function* bar() {
  return yield 123;
}

var f = foo();
var b = bar();

// Start the generator and advance to the first `yield`
f.next(); // {value: 123, done: false}
b.next(); // {value: 123, done: false}

/** Now that I'm at a `yield` statement I can pass a value into the `yield`
 * keyword. There aren't any more `yield` statements in either function,
 * so .next() will look for a return statement or return undefined if one
 * doesn't exist. Like so:
 */
f.next(2); // {value: undefined, done: true}
b.next(2); // {value: 2, done: true}

Notice that f.next(2) will return undefined as a value, whereas b.next(2) returns the number 2.

This is because the value we’re passing into the second .next() call is sent into the to the bar() generator, assigned as a result of the yield expression, which appears after a return keyword; that means the value we send into the generator is returned back to us and finishes the generator.

foo() has no explicit return statement, so you don’t get the value back (foo() already yielded its only value, so the generator is finished, and the result is { done:true, value: undefined }`.

Note that iterators don’t typically consume this return value, even though this answer demonstrates how you could manually get the return value using .next()

Hope that this helps!

Leave a Comment