Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using return in ES6 generator function

I am having trouble finding out what happens if you use a return statement instead of yield.

function *gen(){

 const val = yield someAsyncFn();
 assert.equal(val,4);
 return val;

}

how does the return behave differently from the yield? I assume the return acts as a normal return statement, but the context of a generator function, does it also call gen.return() as well? Sort of confusing.

Perhaps the above is merely identical to this?

   function *gen(){

     const val = yield someAsyncFn();
     assert.equal(val,4);
     yield val;

   }
like image 563
Alexander Mills Avatar asked May 13 '16 06:05

Alexander Mills


People also ask

Can I use return in generator function?

A return statement in a generator, when executed, will make the generator finish (i.e. the done property of the object returned by it will be set to true ). If a value is returned, it will be set as the value property of the object returned by the generator.

What do generator functions return?

Regular functions return only one, single value (or nothing). Generators can return (“yield”) multiple values, one after another, on-demand. They work great with iterables, allowing to create data streams with ease.

Is ES6 a generator function?

Generator (or Generator function) is the new concept introduced in ES6. It provides you a new way of working with iterators and functions. ES6 generator is a different kind of function that may be paused in the middle either one or many times and can be resumed later.

What is yield in generator function in JavaScript?

The yield keyword pauses generator function execution and the value of the expression following the yield keyword is returned to the generator's caller. It can be thought of as a generator-based version of the return keyword. yield can only be called directly from the generator function that contains it.


2 Answers

return deliveres a return value for an iterators last iteration (when done equals true).

I've simplified your example a bit, since the async operation doesn't seem to be relevant to the question:

function *gen(){
 const val = yield 4;
 return val * 2;
}

var it = gen();
var val = it.next(); // { value: 4, done: false }
console.log(val.value); // 4
var res = it.next(val.value); // { value: 8, done: true }
console.log(res.value); // 8

Whereas without a return value, on the last iteration you will return a value of undefined:

function *gen2(){
 const val = yield 4;
 yield val * 2;
}

var it2 = gen2();
var val2 = it2.next(); // { value: 4, done: false }
console.log(val2.value); // 4
var res2 = it2.next(val2.value); // { value: 8, done: false }
console.log(res2.value); // 8
it2.next(); // { value: undefined, done: true }

Sidenote: As a rule of thumb, there is always one more next call then there are yield statements, which is why there is one more next call in the second example.

Let's say you're using a generator-runner like co, then the value you get after finishing the generator would be the value you return:

co(function* () {
  var result = yield Promise.resolve(true);
  return result;
}).then(function (value) {
  console.log(value); // value equals result
}, function (err) {
  console.error(err.stack);  // err equals result
});

Important: If you are iterating through an iterator, using a for ... of loop or something like Array.from, the return value is going to be ignored (Since you are doing async operations, this is probably not the case anyway):

function *gen(){
 const val = yield 4;
 return val * 2;
}

for (let value of gen()) {
  console.log(value);
}
// 4

In the end, calling a generator just creates an iterator. Whether the final value that the iterator returns is relevant, depends entirely on how you use it.

like image 111
nils Avatar answered Oct 18 '22 22:10

nils


In addition to the thorough answer by @nils, there is one additional way to capture the return value of a generator function, namely as the value of yield* (necessarily inside another generator function):

  function* arrayGenerator(arr) {
    for (const element of arr) 
      yield element
    return arr.length
  }

  function* elementsFollowedByLength(arr) {
    const len = yield* arrayGenerator(arr);
    yield len;
  }

Note the first generator function which returns a value, after it is done yielding the array elements.

The second generator function, through yield*, causes the first generator function to yield all its values. When the first generator function is done and returns, that return value becomes the value of the yield* expression.

like image 4
cayhorstmann Avatar answered Oct 18 '22 21:10

cayhorstmann