Kindly help with "yield" expression

Hi,

I am trying to learn JS generators but cannot understand its basic building block yield assignment structure, the below is not giving the result of return , can you please guide … thanks


function *testName() {
  var firstName = yield "sue";
  var lastName = yield firstName;
  return  firstName + lastName;
}

var x = testName();
x.next()  //{ value: 'sue', done: false }
x.next("John");  //{ value: 'John', done: false }
x.next() //  { value: 'Johnundefined', done: true }

Why i get Johnundefined and not suejohn…

Isn’t yield used for loops. What are you trying to achieve here?

Did you see the documentation?

yield is a type of return, you can yield multiple times, but it’s still a return value. So if you write var firstName = return 'sue'; that doesn’t make sense, neither does the yield version (nor would a throw version, the third kind of return). It’s giving wierd results because you’re mixing the syntax of JS up.

It also won’t save the value of that variable for the return statement that ends the generator; those yield values have already been returned.

You’re misunderstanding how things work, I’ll try to explain when I’m not on a phone

2 Likes

@DanCouper -Thanks, this line has helped a lot, i am not able to see where i was making the mistake but still not sure –

In the above code i now know that the first next() discards all values you pass to it via next and evaluates till yield "sue"; and stops, then when we call next() again , it transfers a value to first name…

I guess that is why firstName value is printed …

I think i need to make another next() with last name and hence another yield before the return both values , something like below i guess


function *testName() {
  var firstName = yield "sue";
  var lastName = yield lastName;
  return  firstName + lastName;
}

var x = testName();
x.next()  //{ value: 'sue', done: false }
x.next("John"); //{ value: undefined, done: false }
x.next("Doe"); //{ value: 'Doe', done: true }

But i am still confused about ---->x.next(“John”); //{ value: undefined, done: false }

Ok, so you have two yields and a return (which works basically the same but aways terminates the function). So you get to use next() three times, then the done property in the object that is updating each time flips to true and you’re finished. You are passing values in and as luck would have it the way JS evaluated things meant you ended up with values coming out instead of errors, but you’re getting results via luck not intent. I think that you think generators are like async/await. They aren’t. They’re useful in certain situations but aren’t generally used very much, I guess mainly because they way work seems a bit alien to most JS programmers.

In a sense, generators are functions that can be paused; they’re useful for building/iterating over streams of data, and let you write lazy functions that only act when requested to. But you can’t pause normal functions; once you return that’s it. So to get round this, a generator function is used like a class constructor, it’s a factory, you can set it up, then call it to build iterator objects. By calling the next method on that object, you get to the next step, denoted by yield or return. return stops the function execution in a similar way to a normal function. But yield returns the state at the time it is called, and can be used any number of times. You wouldn’t normally pass anything into the function once you’ve started it. Like a normal function, you give it the initial args then run it.


function * returnOnce(x) {
  return x;
}

So, when you execute next() with a function that returns, you get the value immediately and the done value immediately goes to true.

> let ro = returnOnce('foo')
> ro.next()
Object { value: "foo", done: true }
> ro.next()
Object { value: undefined, done: true }
function * yieldOnce(x) {
  yield x;
}

With yield, it doesn’t go to done: true straightaway, you need to call next again (as there could be any number of further yields).

> let yo = yieldOnce('foo')
undefined
> yo.next()
Object { value: "foo", done: false }
> yo.next()
Object { value: undefined, done: true }

Maybe you want a function that stops running after a set number of times?

function * yieldNTimes(n) {
  let count = 0;
  while (count < n) {
    yield `Iteration number ${count + 1}`;
    count++;
  }
}
> let ynt = yieldNTimes(5);
> ynt.next()
Object { value: "Iteration number 1", done: false }
> ynt.next()
Object { value: "Iteration number 2", done: false }
> ynt.next()
Object { value: "Iteration number 3", done: false }
> ynt.next()
Object { value: "Iteration number 4", done: false }
> ynt.next()
Object { value: "Iteration number 5", done: false }
> ynt.next()
Object { value: undefined, done: true }

Or a function that runs an infinite number of times?

function * yieldRepeatedly() {
  while (true) {
    yield 'doing stuff';
  }
}

(generators allow you to have functions that run indefinitely, in a safe manner)

> let yr = yieldRepeatedly();
undefined
> yr.next()
Object { value: "doing stuff", done: false }
> yr.next()
Object { value: "doing stuff", done: false }
> yr.next()
Object { value: "doing stuff", done: false }
> yr.next()
Object { value: "doing stuff", done: false }
...
2 Likes

@DanCouper - thanks, now i understand better, i am reading Nicholas Zakas and Kyle simpson , but due to lack of time , cannot read full books, so when i try some practicals i get confused … This was very helpful explanation …

Amit