31

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;
 }
asked May 13, 2016 at 6:08

3 Answers 3

38

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.

mckoss
7,0747 gold badges37 silver badges31 bronze badges
answered May 13, 2016 at 6:35
Sign up to request clarification or add additional context in comments.

4 Comments

so if I want to return something besides "undefined" from a generator function, then I have to use "return" instead of yield? and what happens if there is a return statement before a yield statement? assume the yield statement would never be called.
1. It really depends on how you run your generator. If you have an async flow with promises (using co or something similar), then the resolution value once your generator has finished running is going to be the value of your return. If you run it manually with next, you are free to decide what you do with the value of the last iteration. And if you use a loop to run it, return values are going to be ignored.
2. A return statement always terminates the function call, even in generators. So no further statement (yield or other) will be executed after that.
Really helpful information, especially the last note regarding for ... of loop. Thanks!
8

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.

answered May 14, 2020 at 9:25

1 Comment

So this doesn't fit well in the comment field, and it isn't an answer, but it seems relevant: rather than creating another generator, it is possible to get the return value by manually iterating over the resulting iterator. So: while (true) { const { value, done } = await result.next(); console.log(value); if (done) break; } ... you can use the if (done) statement with an else clause as well, if the final return value has a different type or requires a different action.
1

You can write a little helper withReturn that includes the return value -

function *mygen() {
 yield 1
 yield 2
 return 3
}
function *withReturn(it) {
 yield (yield *it)
}
for (const v of withReturn(mygen()))
 console.log("value", v)
 
// value 1
// value 2
// value 3

Here's the async..await version -

async function *mygen() {
 await sleep(1000)
 yield 1
 await sleep(1000)
 yield 2
 await sleep(1000)
 return 3
}
async function *withReturn(it) {
 yield await (yield *it)
}
async function main() {
 for await (const v of withReturn(mygen()))
 console.log("value", v)
 return "done"
}
function sleep(ms) {
 return new Promise(r => setTimeout(r, ms))
}
main().then(console.log).catch(console.error)
 
// value 1
// value 2
// value 3
// done

answered Dec 6, 2024 at 19:45

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.