Check out a free preview of the full TypeScript Fundamentals course:
The "Generators" Lesson is part of the full, TypeScript Fundamentals course featured in this preview video. Here's what you'd learn in this lesson:

Mike discusses generators, which define an iterative algorithm by writing a single function which can maintain its state.

Get Unlimited Access Now

Transcript from the "Generators" Lesson

[00:00:00]
>> Mike North: So the next layer up here, and this is where things start to look cool, is something called a generator function, or generators. So generators are great for defining recursive algorithms. And they can be thought of almost as functions that return more than once You can think of it that way so generator is this function star syntax that's not a footnote, that's not like an asterisks function star is not the same as our JavaScript functions.

[00:00:34] The way normal JavaScript functions work is you basically guarantee that they will run to completion. So most JavaScript programs run using only one thread and it could be argued that, you could say all JavaScript programs run using only one thread and some programs involve launching other programs. Like if you're using a web worker that's a separate script.

[00:00:58] You might argue those are like two separate things talking to each other. The coolest thing about these generator functions is that, we can pause local execution within the function, leave it and then come back later and resume. And that is what's happening potentially, when we call yield. Now, it's not happening here.

[00:01:25] Well, no, it is happening here, in that basically when we yield, we are returning a value out of an iterator. And then when we're asked for our next value, we sort of pick back off where we last yielded. And resume execution again. So I'm going to demonstrate this again in Chrome, but, I just want to make it clear.

[00:01:52] Generator functions returns an iterator and the state of the closure is preserved between next calls. So for example, in this Fibonacci sequence. Generator functions and I'm actually gonna give you this exercise to play with for about 10 minutes. The answer is right here, but I want you to like have a chance to get your hands on this and have tests that you can pass and ya know experience working with it first hand but last my terribly named variables there.

[00:02:27] Those will, that closure and the value of those variables will be preserved as we leave this function and come back into it, back to Chrome.
>> Mike North: I'm gonna use this handy little snippets thing, which is fantastic. It's snippets Let us write a little bit of code, and then run it below.

[00:03:06] Just hitting this little.
>> Mike North: This play button here. So generator functions are much easier to teach these days now that Chrome supports them natively and we can sort of just step right through them. So let's say that we've got our, we've got an iterative sequence of sorts. So we can say function* mySequence.

[00:03:32] And we're gonna say, so x is, we'll just have it like, we'll have it Emits successive powers of two. Two to the one, two squared, two cubed, etc. So we'll call this exponent or power. I'll start with one. So this might look like an infinite loop, but as long as we pause execution in the middle.

[00:04:00] It basically just finds in a sequence of infinite length.
>> Mike North: Yeah I thought it was Math.power. My other complete here is trying to trick me. Okay, so if we've got this, and we run that, so now this generator function mySequence is available to us below. And I wanna set a break point, actually sorry, let me refresh the page here.

[00:04:40]
>> Mike North: So if I've got,
>> Mike North: I just wanna make execution a little bit more clear. Give myself some indications of stuff happening.
>> Mike North: Actually I shouldn't need all of that, but we will, we'll just do that. That's great. So I'll run this. My sequence has been defined. So we'll say, let it equals my sequence.

[00:05:20]
>> Mike North: So it may be surprising to some, if these were a normal function, what would happen if I did this? I guess that's probably the fact that may be a surprise here. Most would expect that I would hit this break point. [SOUND] But I don't. And the idea here is we've basically, we've got the interator that this generator function returns.

[00:05:49] But until we ask it for its first value it's not going to begin executing code within the generator function. If we call it next, now we're gonna hit that break point, and we're basically gonna keep going, keep executing code until we hit that first yield. We're gonna go execute that, we're in this a while through, basically we set up some initial Stuff.

[00:06:16] And then we enter this loop that will infinitely keep emitting values. So we're gonna console log before yield and we'll see that pop out in the bottom of our console here.
>> Mike North: So, I had my buttons, so that's perfect. So I'll hit next function call. So there's our before yield popping out.

[00:06:43] And now if we're gonna say step over next function call, now you see the value just popped out and we're no longer frozen at a break point. So think of it as we've stopped right there, right where my cursor is to the left of the y and yield.

[00:06:59] We've frozen there and we are paused and like I should do this sorry, this is a more meaningful example here, it's not gonna change anything we've done up until this point,
>> Mike North: So there's our value. And now, when we ask for the next value, we're basically going to begin execution on line six.

[00:07:32] We're going to pick up where we left off. And we'll be there. And if keep stepping, we're gonna turn around and go through the why loop again, basically running until we hit the next yield and then we've emitted a new value. So this is a much nicer way of defining an iterative algorithm.

[00:07:51] And we have the potential here to define infinitely long sequences, or things that you couldn't represent as a list. Where maybe it's like 360 degrees when then you turn back around and go to zero again and it's sort of like a ring sequence that you can go around and round and round, right.

[00:08:12]
>> Mike North: So does everyone like? This is pretty new to most people, Python has a similar concept, it have generators in Python. But the point here is when I talk about execution being paused, basically we run in the generator function up until we yield something, right, and then, we paused.

[00:08:36] Because we've just like reached our first value and rekindled out. And then when you ask the iterator for the next value, you go back in exactly where you left off. And you emit your next value, until you hit or and you play forward until you hit your next yield.

[00:08:54] And that’s your next value.And you keep doing that again and again and again. Yes.
>> Speaker 2: So when you, second time around when you hit line 2 and you have that variable assignment.
>> Mike North: That happens once, only for the first.
>> Speaker 2: So all the signs only happen the first time, period.

[00:09:12]
>> Mike North: Only because of the way I've defined this function here.
>> Speaker 2: It's only because [INAUDIBLE] in the while loop, basically?
>> Mike North: Yes, cuz when I ask for my first, let's restart here, when I ask for my first value, grab my iterator, grab my first value. So basically the rule here is, you start executing lines of code until you hit a yield.

[00:09:39] And in this case, we're starting at the very top of the generator function right? And so, we're going to grow through that first step there and we're gonna go like that and then yield and there's our value. And the next time we do it again, we're basically caught in this loop that we're forever gonna be caught in.

[00:09:55] And that has more to do with the fact that we're in a wild through, than the nature of the generator function.
>> Speaker 2: Got it.
>> Mike North: But this would look like a funky function for sure, this would seize your browser up. Except for the fact that, at every yield that indicates that were sort of releasing the value and we can just ask for as many things of this as we want, until we run out of the ability to represent powers of tools as JavaScript numbers.

[00:10:23] If I did something like this
>> Mike North: And we first.
>> Mike North: I'm just gonna set break point everywhere so we can see really easily. So we grab our iterator. Next is going to be frontend. There it is. Masters, workshop, and now we're done. So the iterator is done once we reach the end of this generator functions block.

[00:11:10] For the end of the function scope. And it is regarded as like, we have finished, or you can return, you can also return.
>> Mike North: Sorry, and my linter would probably say, what is that last yield doing there? Like or in this case, there is sorry I have to run the snippet, there is our iterator I'm gonna hit next, we can hit there is front end masters, website

[00:11:52]
>> Mike North: And then see how it says done true? I wanna jut let it play, but does that make sense? So now, we said, when you hit return, it's basically saying, here's your last value and by the way, I know that I'm finished. Like if I ask for the next value here, it's gonna say

[00:12:09]
>> Mike North: Well, it is not defined. I might be in a different scope here, cuz that's where my last debug of this. So now it's saying, as many times as I ask for it here, I'm not clearing, just See that I keep going there. It's just gonna say look, I got nothing for you.

[00:12:31] It's almost like, it's just sort of a dead end at this point because we've returned. So those are the two situations within a generator function where the iterator that it returns says I got nothing for you.
>> Mike North: Does that make more sense now?
>> Speaker 2: Yeah it totally does.

[00:12:48] So one thing that was really nice about the return though was that the last object, the last time you called next, it actually gave you a value. And done was true whereas with the yields you had to get that extra object with the value undefined, and the done true.

[00:13:07]
>> Mike North: That's true. So let me see if I can shed some light as to why it works the way it does.
>> Mike North: Okay, so if we have a situation like this. So I'm gonna refresh Grab my function, all right, there is our iterator. So the first thing we're gonna say is, obviously we're gonna say Frontend,

[00:13:41]
>> Mike North: Masters, workshop. Now at the time I return this next value here, True or false? There is more code to run?
>> Speaker 2: True.
>> Mike North: Right, true, there is more code to run. So I'm gonna play this through, and honestly, we're still in JavaScript, right? We don't know whether potentially several lines below this line of code.

[00:14:05] There may be another yield. We do know we're not the last line of code In this scope.
>> Speaker 2: Nice.
>> Mike North: Right, or we don't even know that, but, there is no way to know necessarily whether after this.
>> Speaker 2: Yeah.
>> Mike North: So, at this point now we're gonna see no value returned.

[00:14:24] But that last little bit of code's gonna run. And it's still gonna return undefined. Or it's still gonna emit undefined.
>> Speaker 2: Awesome, thank you.
>> Mike North: Does that make more sense?
>> Speaker 2: It makes a lot more sense, yeah.
>> Mike North: Because at a return, you know you're done. That is what a return means.

[00:14:38] Returns, it still works in the way you would expect it to work in this generator function, meaning. Within that function scope, like you are calling it, the game's over. Yield makes no such assumption. In fact, it has to assume the opposite, it has to assume there's a possibility that there may be other stuff there.

[00:15:00] Now a for of loop will take care of that for you. It will basically handle both cases the way you would hope it would handle both cases, so in this case, if we do something like this, then we do little more space so for let v for value of mySequence.

[00:15:46]
>> Mike North: So I know it's a little confusing here. Let me see if I can make it a little bit more obvious,
>> Mike North: Cuz console logs from within the function versus from the outside. So these are the ones here front end, masters, workshop, those are yielded out, and that is being logged as a result of this.

[00:16:14] You'll note that it did not attempt to log that last value with this little caret here. It did finish the iterator, it just basically finish the iterator and decided that it does not need to invoke that block expression that is attached to that foreloop. It will get the next thing and it will basically say, all right, and I ran this iterator and the values are undefined and we are now done.

[00:16:42] So you do not really get another turn of this loop.
>> Speaker 2: Is that because it was undefined or because it was done?
>> Mike North: So let me see if I can express it a different way, to make sure we're 100% clear on this. So the second to last value here workshop Right for the second to last thing it pulled off of the iterator was value of workshop done false.

[00:17:10] So think of it as internally within the implementation of for of, it's gonna pull the next value on the iterator. And recognize that it is not in fact, we're not yielding undefined, that doesn't really make any sense. So it will finish the iterator, but you don't get another turn of the for loop, right?

[00:17:33] The interesting thing is if we do this
>> Mike North: Interesting. Okay, so now we're seeing it expects the iteration to behave the same way. It expects the iteration to behave as it does in the yield case, and it return as regarded as like, so it basically says when it see it's done, it is saying [CROSSTALK] value was the last one.

[00:18:06]
>> Speaker 2: Right. So the only way to really, it's yield is really my iteration functionality, not, yeah.
>> Mike North: You may want to use return as a tool for as early termination because that is a way to sort of say.
>> Speaker 2: But that's not my way to get back to the function.

[00:18:21]
>> Mike North: Yeah, so this hopefully gives some clarity around this now, sweet, because we are going to be playing with this today And this is, this is a cool new JavaScript language construct that opens a lot of interesting doors here. So as we saw, execution is paused, right? Between times that we pull items out of that iterator.

[00:18:48] And generator functions and iterators they are very closely related. So this is great for defining an iterative algorithm, especially ones that maybe we don't necessarily know the, they don't have a well defined length