Check out a free preview of the full The Hard Parts of Asynchronous JavaScript course
The "Generators" Lesson is part of the full, The Hard Parts of Asynchronous JavaScript course featured in this preview video. Here's what you'd learn in this lesson:
Will introduces the concept of native JavaScript generators by walking through a reconstruction using arrays based on previous lessons.
Transcript from the "Generators" Lesson
[00:00:00]
>> Will Sentance: Now we move onto, I said once we rethink about data as a flow of data, a stream. Once we think about data as flows where we can pick off an element one-by-one, we can rethink how we produce those flows. JavaScript, now lets us produce those flows using a function to set what individual element will be returned next.
[00:00:20]
Unfortunately, before we get to that we gotta go back to something that, honestly is a little bit annoying. But we do need to understand it for us to use the built-in functions that produce flows. Cuz they don't call a function directly return next element, they instead have an object with a function on it, a method.
[00:00:42]
Next, that when called will kinda give us a next element. I wanted to build that from scratch ourselves, a final reinforcement of this model. But also to ensure we understand that this is just a function that's in an object now. But it's no different to what we did before, this should feel hopefully very clean review.
[00:00:59]
But it will get us back up to speed before we move on to creating these flows using functions rather than underlying arrays of data. We're gonna create a flow where we call a function each time, get the next element. Here we're doing it with a static, pre-built collection of data.
[00:01:15]
In a moment, we're gonna do it by yielding intermediate returns, you could think of those out each of our next elements. Here it's gonna be directly analogous. We're gonna discover it gives us a lot of control over what those next values can be. Cuz now we're just running code to generate those next values, it's very very cool.
[00:01:36]
But first let's just be clear, JavaScript's build in return next elements, iterators they're called, are actually objects with a next method that when called return our next element from the stream or flow. So we don't call return its element, instead we call the method on it. So returnNextElement's now an object, not a function, but it wraps a function.
[00:01:57]
So we just call the function inside, but let's build it ourselves. Let's restructure it slightly to make sure we're truly clear on what this is doing. All right, well this should be fairly,
>> Will Sentance: Solid for us, cuz we just did this already. Sony, line one, what are we doing?
[00:02:13]
>> Speaker 2: Creating a function named createFlow.
>> Will Sentance: Excellent, there it is, createFlow. Next line, Brian?
>> Brian: We are declaring a constant called returnNextElement, which is undefined right now but it will be the return value of createFlow.
>> Will Sentance: Excellent type of communication from Brian. I think we can all appreciate I knew everything explicitly that this is gonna do.
[00:02:38]
It gave me a strategy, that was very good technical communication people. Okay so return.
>> Will Sentance: Next element will be the output the return value of calling createFlow with the input of the array, 4, 5, 6. So we hope that this function that's gonna come out. We hope that it's gonna return as a function into, no we don't.
[00:03:05]
We hope it's gonna return as an object with a function on it, with the label next, into returnNextElement that's gonna allow us to call returnNextElement.next(), and return out first, what element?
>> Will Sentance: Ben, what element?
>> Ben: 4.
>> Will Sentance: 4, we're gonna call that next function again, the next method again we're gonna get out?
[00:03:28]
>> Ben: 5.
>> Will Sentance: 5, and then 6 exactly, that's what we hope. Well, that means that our return next element object with a function better have a bundle of persistent data, the underlying array and the positioning element, the I, attached onto it. It's going to but let's just make sure.
[00:03:47]
Okay, all right, into the execution as we go. It has a memory, what's the first thing in the memory, Victor?
>> Victor: I.
>> Will Sentance: That's the second thing in our memory.
>> Victor: I'm sorry, array.
>> Will Sentance: Array, yep, great, which is?
>> Victor: Arguments stuff, I'm sorry. [LAUGH]
>> Will Sentance: Yeah, it's our argument, you're spot on which is the value, the array 4, 5, 6.
[00:04:11]
4, 5,6, Victor, please don't apologize, you were spot on, it's very nice. Okay, excellent, so, now we put this stuff here, well of course, I hate to use the phrase of course. You put this stuff here because we're going to hopefully define a function here that when we turn out will pull out on the back of the function in his backpack, that live data, and attach it in the backpack to the function we return out.
[00:04:39]
We're not gonna define the function independently and return it out. We're going to instead define the function as, what Ben?
>> Ben: As an object?
>> Will Sentance: As a method or an object, exactly. So that object Ben is gonna be called what inside to createFlow?
>> Ben: Enter.
>> Will Sentance: There it is, and it's just a wrapper, it's just wrapping our function.
[00:05:03]
But even though our function's being defined not independently but as a method or an object it still gets all of that same bond, thing we talked about before. It still gets the same bond to all the surrounding data as soon as it's defined through what property? Paul, can you remember the property?
[00:05:23]
>> Paul: Closure.
>> Will Sentance: Well, okay, so we mean through the JavaScript property what's the actual hidden property, James?
>> James: Scope?
>> Will Sentance: Scope, exactly. It's this little square bracket square bracket, which means it's built into JavaScript under the hood features scope property that bonds to that surrounding memory, excellent. Good, now we'd return out what would we return out?
[00:05:46]
Josh, what are we returning out of the bottom of this createFlow function? What's the sage return out? Final creep flow?
>> Josh: The bottom great flow.
>> Will Sentance: You mean the inner? Return out inner which is-
>> Josh: The object.
>> Will Sentance: The object, excellent. Thank you so much George, that's been out returns the object with the, dear, I genuinely feel bad now with the function here.
[00:06:13]
But the function's brought with it its surrounding data, and we're going to store that in what global constant, Blessing?
>> Speaker 9: returnNextElement.
>> Will Sentance: returnNextElement. Which maybe isn't the perfect name for it now, I'll grant. It's now better to be thought of as, I wanted to call it this but I didn't wanna write the whole thing out every time.
[00:06:32]
I wanted to call it, function, sorry, object with returnNextElement method, but it was too long to write out, but that is what I really wanted to call it. I actually did change the slides to that for a bit and thought, eh-eh, that's getting silly, it was like running half the way across the page.
[00:06:49]
I though it was maybe not smart but there you go, next is this method. And that function has a back pack of data but they object got put out with the function on it, it blew with it. All that surrounding data array 4, 5, 6, and i is 0.
[00:07:13]
It put all of that out on the function, even though it's no longer just a function, it's a functional object, it still gets all that same thing happening. Objects don't, it's only functions, because when that function gets called, returnNextElement.next parens, runs that function, it's gonna have access to that backpack of data.
[00:07:31]
It's the only way it gets access to data. By the way, to be really clear we can't go return its element.next.array. This is state, this is like live data attached to the function definition. Okay, we've just finished returnNextElement to the return value, createFlow which was that object with next on it.
[00:07:50]
And so what's our, we come back to global execution context we're updating our next global line of code as what?
>> Abdee: Declare a const, element1.
>> Will Sentance: I always ask the same people the same question. Do you notice this, I cycle to the same people. Excellent, Abdee, you are spot on and I've asked you that now 3 times.
[00:08:08]
So well done, Abdee, it's fascinating. He's very consistent.
>> Abdee: It's because subconsciously I'm doing I'm sort of making subconscious decisions about who to ask.
>> Will Sentance: Excellent Abdee, you're spot on. We declare element1 and it's going to be the return value of calling what, Abdee?
>> Abdee: The returnNextElement function.
[00:08:31]
>> Will Sentance: Yeah, which is not a function anymore, is it? It's an object with a meet a function on it under what property name?
>> Abdee: Next.
>> Will Sentance: Exactly, so let's just walk through that. I wanna make sure we know how this sorta thing works. Paul, where do I look for returnNextElement, the object?
[00:08:46]
>> Paul: In the global context?
>> Will Sentance: In the global memory, yep. And what do I look for on it?
>> Paul: The next method?
>> Will Sentance: The next property I'd say. You're right, yeah and I hope it is a method because we've got parenthesis on the end. Is it, it is, excellent.
[00:08:59]
But it is not, [INAUDIBLE] know it is a method so to speak at this point, it could try to put parenthesis on the end of anything, but it is indeed a function, a method and execution context. So we're not gonna do both of these, we're just gonna do the first one into it we go, and let's get this spot on.
[00:09:17]
So Sean, first line of code inside the next method.
>> Sean: Const element is declared.
>> Will Sentance: Okay, element is declared, excellent, exactly, element is declared. So element is going to be, and remember we're hoping this function is gonna return out 4, that's what we're hoping. Element is gonna be array position I.
[00:09:40]
We do not know what these two 1s are here. Where do we look first Shawn, for array and I?
>> Sean: We look into the function next to itself?
>> Will Sentance: Yeah, exactly, the execution content, the local memory, well done. Did we find it?
>> Sean: No.
>> Will Sentance: No, and we hopefully wouldn't right because we want these to be persistent between function calls.
[00:09:59]
And if were looking here finding it then that will be a brand new data each time we call the function. So we wouldn't be persisting our data at all, instead we look next out to the next functions, backpack of persisting data, where should do we find?
>> Sean: We find array and I.
[00:10:19]
>> Will Sentance: Our raised value is?
>> Sean: 4.
>> Will Sentance: Array's value?
>> Sean: I'm sorry, 4, 5, 6.
>> Will Sentance: And i's value is?
>> Sean: 0.
>> Will Sentance: Perfect, and we switch them in here. We put them in here, let's do them in green to show where they're coming from. 4, 5, 6, position 0, which gives us an element, what value Shawn?
[00:10:47]
>> Sean: 4.
>> Will Sentance: 4, but now we have another line. What's the next line in our returnNextElement.next method?
>> Sean: Add 1 to i.
>> Will Sentance: Add 1 to i, increment i from 0 to 1. Again, not in local memory, but in the backpack.
>> Speaker 2: Always surprising, right, because we're calling returnNextElement.next.
[00:11:09]
We're calling it in global but the back pack of data, shoe horns itself in before we reach global to looking for the data. The back pack of data shoe horns itself in and that's where we look first. And there we also put i to 1, and the final line of the next method has to do what Sean?
[00:11:28]
>> Sean: Return that value.
>> Will Sentance: Return that value, the 4, and that's it, stored in what global constant?
>> Sean: Elements.
>> Will Sentance: I'm sorry, what did you say?
>> Sean: Stored in what global constant?
>> Will Sentance: You're close.
>> Sean: element1.
>> Will Sentance: element1, excellent. And we know that the next call to .next is going to look in the backpack, find I is now one.
[00:11:53]
And then it's gonna return us at what value hopefully, Shawn?
>> Sean: 5.
>> Will Sentance: 5, exactly. So in element2, we would return out 5, okay. So basically, review there but the reason we did it was to recognize the design of built in. Iterators, they're actually objects with a function on them that's gonna do that stuff, okay, as opposed to just a function.
[00:12:21]
All right, and by the way, built in iterators actually produce the element that gets returned out, not as a number. You never let us keep it easy man. They instead produce out an object with a value which is the number we care about. And then another property called done, which is false until we've called returnNextElement.next.
[00:12:48]
Again this time 5 ,still done is false, next time six, still done with fold. Next time the value is undefined and done is true.
>> Will Sentance: I guess that's valuable if you wanna check whether your iterator is finished because you could have a position in your collection that was undefined.
[00:13:08]
And if you were just checking for undefined, cuz you hit the end of the array, and you had an undefined element, then you would end your grabbing of elements too soon. So it makes sense to have a property done that actually switches the files once you've finished going through all your elements.
[00:13:26]
And by the way, we could easily augment our create flow functions inner that gets returned to returnNextElement to not return out the element, but instead return out valued is 4. The value is the element, and done is false until it's true, based on the condition of going through the array and checking if it's finished or not.
[00:13:48]
And we can very easily augment this, but that is exactly how they're built out in JavaScript. We need to do that cuz we're now gonna start using built-in tools to give us these returnNextElement.next functions that when run will give us each of our elements one by one. And we're gonna produce those flow of data, not from underlying collections 4, 5, 6, but instead we're gonna produce these flows using a function.
[00:14:21]
We're actually gonna define a function that has kind of intermediate returns, the best way of thinking about them. They're statements that return out our next element in our flow, just like we did when we grabbed it from a collection. Behind the scenes is collection we just see the returned out element, next element.
[00:14:42]
Now we're gonna get those elements out, not from a collection but instead from a function as kinda intermediate steps in that function's execution. It's really interesting, kind of this fusion always increasing fusion of data with functionality. Now we're producing flows of data not from a collection of data.
[00:15:03]
But instead from a function where we evaluate statements and pull out the evaluated result as our intermediate, as our elements one by one. It's gonna be very, very interesting how this works. How this code works is going to absolutely undo everything you thought about how JavaScript's execution model works.
[00:15:23]
Not undo, it's consistent with it, but it's going to add a behavior that you never thought would be allowed, that I never thought would be allowed. It's not crazy, but it is a behavior that we never thought would be allowed. Okay, and it uses this new style of function, the star function, known as a generator function, to produce our flows of data.
[00:15:47]
We're gonna call that createFlow It's gonna return out a special generator object into returnNextElement,
>> Will Sentance: With a next property on it. And when that next is called, you wait for it. It's gonna take us back in to continue our execution of that createFlow function. It's gonna return out our result, and that's gonna be our first element out.
[00:16:14]
We're gonna walk through this line by line and really get this precise. I'm hoping my board is white enough for this because it's a fascinating new way. And again, not inconsistent with our more execution. It just allows us because we're already very used to attaching to function definition, persistence state.
[00:16:33]
We're just attach persistence state and a tracker of where we are in a function execution.We'll see us in a second.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops