Test Your JavaScript Knowledge

Generators & Iterators

Lydia Hallie

Lydia Hallie

Lydia Hallie
Test Your JavaScript Knowledge

Check out a free preview of the full Test Your JavaScript Knowledge course

The "Generators & Iterators" Lesson is part of the full, Test Your JavaScript Knowledge course featured in this preview video. Here's what you'd learn in this lesson:

Lydia demonstrates how to create a generator function using the asterisk symbol and explains that a generator function returns an iterator object. The instructor also shows how to use the iterator object to call the next method and retrieve the yielded values. They discuss the difference between regular functions and generator functions, highlighting the ability to pause and resume execution with generator functions. Additionally, the instructor explains how to create custom iterable objects in JavaScript by implementing the symbol.iterator method.


Transcript from the "Generators & Iterators" Lesson

>> So we're ready to continue to the next section then on generators and iterators. So just everyone knows what we're talking about. We can create generator functions with the little asterisk after the function keyword. And essentially what this does is a generator function returns an iterator. And this is a special type of object that contains methods like next, return, and throw, and it also has a state.

And with this iterator, we can call the next method, which kind of just like changes the state you're running, and then returns the value that gets yielded from the generator function. So in this case, we yield one. So whenever we call next, the value is 1 and done is false because there are still more values to be yielded within the generator function.

So, whenever we call it again, hello, yes, it changes back to running and then it returns that value. But we have to explicitly invoke next to actually do that because with regular functions, it's like kind of this all or nothing approach. Whenever we call a regular function, it will just execute that entire body.

There's no way for us to kind of pause it in between. With a generator function, we can. Yeah, and it'll just keep doing this until we actually returned from the generator function either explicitly or implicitly, in which case done is true. Now, the cool thing with, and then it's closed, with iterators is that, or with generator functions is that we can then create iterables.

So we can use the spread operator on the iterator that gets returned from the generator function and use stuff like for of. And you can see that whenever we do that, only the yield of values get logged, so not their actual return value. So this question is pretty much the same as this example that I show here.

But question 21 is what gets logged. So we have the generator function count that yields 1, 2, and then returns 3. And then we have a for of method that logs the value of the returned iterator object that gets returned [LAUGH] from the count generator function. So with that log, either 1 2 3, 1 2 Promise, 3, 1, or 1 2.

The right answer here is 1 and 2, E. I'm just going to copy this and show you what's going on in the console. All right, so we have this generator count function, but when whenever, or actually, I'm going to set the iterable to count. So again, whenever we invoke count, we're not running any function or anything, unlike if we had like function count, this would execute the function, that's not the case with a generator function.

So now, you can see that it is suspended, that's kind of what i just tried to show you and then it has the prototype generator and this contains next, return, and throw. And this gets used by methods that need an iterable or that iterate over iterable. So the const, or for of, or using the spread operator,.

So we can use like it, this is 1, 2, or that const value of it. Well, actually, so if we do it again now, you can see that, or, okay, let me just log this, and you can see that it's closed now. And that is because we, the iterator itself, we've kind of exhausted now.

We've already, yielded all the values, so now it's closed. Of course, what we can do is just create another one, so it2 is count again. And this is fine, we can just iterate over it2 again, that's fine. Cuz like we just created a new object based on that generator function.

But once it's closed, once we've already yielded all the values, we cannot reuse the same iterable. Anyway, so 1,2 is here and that was also the answer for this const of. Question 22, to create a custom iterable objects in JavaScript, so we have a normal object and we wanna make it iterable, the object must implement the Symbol.iterator method that returns an iterator.

The iterator must implement what method? Is it next, iterator, iterate, forEach, or is it just iterable as is, so we don't have to add anything? And the right answer to this is next. And we just saw that on the prototype. But yeah, so we have a regular object here which by default isn't iterable like we cannot use for of or use the spread operator on an object.

But we can make it an iterate by implementing the iterator protocol. And we can do that with Symbol.iterator. So this returns another object that must adhere to that protocols and that has to return the next method. What we can also do is add the little asterisk in front of the symbol iterator and this actually makes it a generator object that already adheres to that protocol anyways.

So in that case, we can make a normal object that isn't iterable by default an iterable. So that can be pretty useful if you have a lot of data or the object contains data and you wanna somehow just map over it or maybe be like, yield values based on specific conditions.

In that case, adding or making it iterable can be very useful. What's get logged with the following? So we have an async generator function with range, start, and end and that, yeah, we have a for loop that creates a loop and it yields the result promise for the index.

And then we have a generator or a gen iterator, where we have range 1, 2, 3, and then we have a four away const of loop that logs the item. All right, and the right answer is C, it's 1 2 3. Cuz essentially like what this is, or at least how I see it in my mind, simplified with the same ideas, that we have that async generator function that just yields the resolved, or a resolved promise every time.

And we can use it for await of loop to yield promises instead of just like regular non promise values, but it awaits that result promise. So when we await a promise, item is equal to the value that we pass to resolve. So in this case, it'll just log 123 because we're already awaiting the result value.

So, yeah, just an FYI that we can also have async generator functions. And of course, like with generator function, what is nice is that if you're reading a lot of data or you're reading a file or anything that comes into, I don't know, just network and you wanna filter it or you wanna find certain data.

You don't have to have everything in memory before you can start mapping over that data, filtering it, cuz by yielding it or just kind of by splicing it up every time you can say, okay, well, I've already found it. I don't need to load everything into memory already, I can just only do it when I really need it when I haven't found that value yet, then you can automatically or you can manually invoke next again to get more data every time.

That's kind of the main, I don't know, use case for iterators in general, just to map over a lot of data. Okay, so here we have another generator function gen1, yields 2 and 3. And then we have a gen2 generator function with a yield with an asterisk. So what would this log if we log with a spread operator gen2?

Is it 1 2 3 1, and then an array of 2 3 4, a SyntaxError, or 1 4 3 2? So the answer here is A, it's 1 2 3 4. So the thing is that we're delegating another yield or another iterable by having that yield with that asterisk.

So essentially what we're doing, dang it. I'll just go here. It's already open. Is that we're essentially saying like this, it's pretty much the same. Like it goes to this yield and whenever we have this one, it then turns here, yields 2, 3, and then 4. This could have been anything.

This also could have been, I don't know 1, 2, 3, is it? I'm just gonna open this room with that one, index.js. That would have done the same thing, that's fine. It could have been any iterable. It could have even been a string. That's fine. It would just make it part of the current kind of yield.

So in this case, yeah. And this is called delegating a yield. So I don't know, you can kind of just visualize it mentally as if we're just adding it to the generator function like that.
>> If you didn't have the star, would the answer be B then?
>> So then you're just creating that, you're yielding the returned value of the generator function.

And you can see that here is the object generator. Yeah, and no, you can't really expand it. But then you're just yielding that value, so you're not doing anything with it. Let's see what happens when we do this. Then you have a, cuz then we are iterating over that iterable which calls that next method under the hood, spreads it, and that yields this value which is 2, 3.

Yeah, [LAUGH] I don't think I've ever used this in real life or delegating yield, but I'm sure there are plenty of use cases where you do need it

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now