The Hard Parts of Asynchronous JavaScript

Generator Functions with Dynamic Data

Will Sentance

Will Sentance

The Hard Parts of Asynchronous JavaScript

Check out a free preview of the full The Hard Parts of Asynchronous JavaScript course

The "Generator Functions with Dynamic Data" 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 explores the function* declaration, which defines a generator function that allows for dynamically setting data flows.


Transcript from the "Generator Functions with Dynamic Data" Lesson

>> Will Sentance: Josh, line one. What' are we doing?
>> Josh: Declaring a generator function to create flow
>> Will Sentance: Yup. Good. Declaring a special type of function is magic. New type of function Josh gives us called a generator function creates flow. There it is. Special type of function. I've updated my diagramming for it.

[INAUDIBLE] Okay, next line, not gonna ask Bryan cuz I've asked him the same question as well multiple times. Gonna flip it up. I have an idea in my head about what question to ask each person. It's very interesting, very interesting. Rick, what's the next lines of codes here to do?

>> Rick: You define a constant return next element. Which is undefined right now.
>> Will Sentance: Yeah, good, excellent! Return next element, which is gonna be the output of running, it's gonna be output of this call to create flow. What could the output of this be? Well, presumably, we're gonna create an excision context.

>> Will Sentance: Probably, but no, for now, not in any meaningful sense to us. So, we're in global, I'm not sure if I need to keep track of this one, we don't need to keep track of the cost that desperately. We need as much space as possible, so okay, so return, return, next element.

It's gonna be the output of calling create flow. Now, Ben, so we're hoping the output return element is an object with a method called next. That when I call that it's gonna give me my first element in my flow of data. What do you think that's gonna be then?

>> Ben: Four.
>> Will Sentance: We hope it's gonna be four. Okay, next time it's gonna be five, then it's gonna be six. But how are we gonna get that? That's the interesting question. So, create flow does not go inside create flow's execution context. Instead, it returns out a special generator object with a next function on it.

That when called, is gonna do something fascinating. There it is, with next. As the function. Okay, that's been stored and return next element. So now, return next element has a next method on it. Because it was the output of running create flow. We've now finished this first, this call to create flow.

So now our next line of code says to do what? It's gotta be only one person.
>> [LAUGH]
>> Will Sentance: [INAUDIBLE]
>> Abde: Inside.
>> [LAUGH]
>> Abde: Yeah, we're just declaring it in memory again.
>> Will Sentance: Yeah, yeah. You're getting too complacent about your full responsibility. We're declaring what?
>> Ben: Because element one.

>> Will Sentance: Degrade of course at element one. Excellent, thank you. And we're now gonna call the method on this return next element object, the next method.
>> Will Sentance: And what's it gonna do?
>> Will Sentance: Here's what it's gonna do, people. You never thought it would happen. It is gonna,
>> Will Sentance: Execute or begin execution.

It's gonna open the execution context of create flow.
>> Will Sentance: This function on the object that got returned out of create flow has an intimate bond to where it was born at create flow, that when we call the next, it's gonna start initiate calling create flow, the function from which it was born.

It's gonna start calling it. Now we'll talk in a moment what that literally means. What is calling a function? What is executing code and how do you keep track of that? It's not as profound and bizarre and kind of against the rules of Java as you might think.

Okay, so when it does that, it's going into the [INAUDIBLE] To create flow. That is what it is. So what's the first thing it does? Actually, it's a pretty boring function. There's no memory allocation happening. What's the first one it hits, Michelle?
>> Michelle: Yield four?
>> Will Sentance: Yield four, okay, yield is a super powerful keyword just like a light return that exits out of the function.

But it's suspending the execution context. It's not ending it. What that means and we'll talk about that in a second. Again, it's not like it's sitting there on the core stack. Kind of it's sort of there. It does something else. I wanna make sure we get that clear, what it actually means to spend an exclusion context.

But this is a return statement, essentially. We are gonna grab that four and we're gonna yield it out as the output of our call, which stimulated create flow to call. It hit the yield statement inside that four is gonna be not our output or create flow, that was create flow's never gonna give output again directly.

It's only that returnNextElement, took us in here. And the yielded result is in the output of that statement we just called Actually, I might do a dotted kind of line here to show that create flow's kicked off. So, this is an output of which is four.

And that's gonna be assigned where, Blessing?
>> Blessing: To element one?
>> Will Sentance: To element one, excellent. There it is in element one. Beautiful. Crazy. I'll tell you this, we've ended up here with our flow of data, the first element for it, very nice. Right now we're gonna see that it doesn't feel that much different.

We've got four, five, six. But in a moment we're gonna see, we can dynamically control what our flow of data comprises. Cuz it's just code now, that we can set what our data flow comprises. All right, so, next line of code, Alec. We're back out to, I should say, sorry.

We've paused. I don't even like the term pause. But we've suspended, we've put on hold the execution context here. Well, that actually means, I might as well say now, what that actually means is, no, I'm gonna show you on the next slide. So, for now, okay, Alec, we come back out to our global execution context where we hit the declaration of what?

>> Alec: Element two.
>> Will Sentance: Yep. Which is gonna be?
>> Alec: The output of
>> Will Sentance: Correct, but we have total output, right? The output of is not gonna be the conventional sense. It's gonna take us back and create the floor, whatever yield's next is gonna be output of, exactly.

So let's do it,, which will take us in here where we hit what line, Alec?
>> Alec: Yield five.
>> Will Sentance: Yield five, and that five is then gonna be our output of this statement, actually this expression, which has being called, which is therefore five. And we saw that where, Alec?

>> Alec: Element two?
>> Will Sentance: Element two, look at this, our flow is coming It's really nice, our flow is forming. We call return at element on next one more time. It's gonna give us out six. We return it one more time, it's gonna give us out undefined. So, first thing to say, we now get to produce our flows using a function.

What that allows us to do, is dynamically set what data flows out to us when we turn on the tap and give ourselves a next element. When we turn on the tap and give ourselves a next element. We can dynamically set that with code. We can even, we'll be able to see down here, set it from outside.

We can sort of return back and get the execution context with new data to determine what the next result of our flow of data's gonna be. The next element from our flows. We get complete control over what the next element in our flow is gonna be. Unlike when we built them using a baseline array, four-five-six, that was set, we couldn't control that, we couldn't have logic determining what the later elements would be, that was just, it was what it was.

Now we're gonna get to control what the later elements are gonna be. It's going to require some really precise thinking to follow though, because it has a behavior. That does feel weird. It's actually not crazy but it does feel weird. Okay. So we're going to clear this code and go through this line by line what we have up here.

And then we'll do thumbs. I don't think there's much point in doing thumbs before we get this dynamic piece out on the table. All right, so.
>> Will Sentance: Okay, line one, Josh, what are we doing?
>> Josh: This one, declaring a generator function of createFlow.
>> Will Sentance: Yep, well done. Line two, what are we doing, Paul?

>> Paul: We are creating a new constant called returnNextElement and setting it to the invocation of the createFlow generator.
>> Will Sentance: That's not a crazy thing to say. I still don't even love saying the invocation. It still suggest that returnNextElement's value is an invocation. It's not. It's the return value of an invocation.

That still makes people think when they go to return its element, I better go back and look and see createFlow's what's stored there. createFlow is never stored anywhere. createFlow's invocation, createFlow parens, is never stored anywhere. If createFlow is invoked, we do the work of it, whatever comes out, that's what's stored in returnNextElement.

Just to be really, really clear on it. So it's undefined for now, let's go do some work. Let's do that,
>> Will Sentance: Work. ReturnNextElement is the output of the call to createFlow, which Ben, do you remember what we said this returns out, kinda surprisingly?
>> Ben: An object with the property of next.

>> Will Sentance: Yes, notice the generator, notice a generator object, exactly. A generator object with a next property, which is a method of function on it. Good. All right next line, Ben.
>> Ben: We're going to declare constant element1.
>> Will Sentance: Let me just get my function, my object with a function wrapped on it, there it is.

Yep, declare element one,
>> Will Sentance: Okay.
>> Ben: Undefined returned value, return next element.
>> Will Sentance: Yeah, which is a weird old return value, it's gonna be very surprising. So element1 is the output of, the call which is just this function here being called. The one which we returned out on this object, and store it and return next element.

This, though, is a command, to begin the execution context of what, Ben?
>> Ben: createFlow.
>> Will Sentance: The createFlow execution context.
>> Will Sentance: Okay, so we open it,
>> Will Sentance: And it has a memory. There it is. And inside of it we go, and what's the first thing that it says to do inside, Brian?

>> Brian: You'd clear a constant named num and assign it a value of 10.
>> Will Sentance: Great, there it is. Okay, next line says to do what, Brian? Left hand side first.
>> Brian: Create a new constant called newNum.
>> Will Sentance: Install that in memory initially. Well we're gonna see. So, newNum is going to be the evaluated result of the right hand side.

That's always the case. If the right hand side is a number, a value, we just saw the number there. That right hand side ain't a number, it ain't a value, it's actually a command. It's actually a super powerful command so yield num which is 10. It's actually a super powerful command.

It's a command that's going to throw us straight out, it's gonna throw us straight out, this value, a straight out of this execution context. And Ben is going to have that 10, that output, be result of what?
>> Ben: NewNum which will be-
>> Will Sentance: No.
>> Ben: No?
>> Will Sentance: Yeah, exactly we declared newNum but yield is so powerful.

The right hand side is so powerful cuz it's a statement to just throw you out of the function. We're never gonna get a chance to store something in newNum. It's undefined for now, the right hand side we was with you declared the left hand side newNum, and the right hand side, you then want to get to a value that you can install on the left hand side.

For the right hand side, we don't have a choice to give it a value, to evaluate to do a value, because it's like seeing return 10. It's like a super powerful keyword that just kicks out this ten as our output into the So what does evaluate to, Ben?

>> Ben: 10.
>> Will Sentance: To 10, exactly. Which gets stored in where then?
>> Ben: element1.
>> Will Sentance: element1, and solution context newNum never got assigned anything. This has been paused just before this right hand side could evaluate to a value and be stored in newNum. So in our execution context newNum is still undefined.

>> Will Sentance: Let's hope we get a chance to assign it newNum something. Let's hope.
>> Will Sentance: Okay we're back out to global execution context. This execution context is paused. We'll talk about what pause means in a moment. I don't like the term pause, that's definitely a feat for what actually is.

Well it's not. But what does it mean to pause a function? What is an execution context. To just give you a preview, execution context is just state, memory, and what line am I currently on, in what position? That's all it is. And the thread is actually in it, as opposed to not in it.

The function line has control over the thread. Other than that, that's all it is, position in the code, and a memory. That ain't that hard to save in the next function, or sorry, it's attached to the next function. That's all a paused execution context is. It's the closure, the backpack, but now also the position in the function's execution.

So we can come back and continue running. That's all we're gonna say, we'll talk about them in more detail in a moment. But for now, we're back out to global where Alec, what's our next line? This is our previous global line, what's our next line of global code?

>> Alec: Define element two.
>> Will Sentance: Excellent, give me a second, element two is going to be a returned value of calling return next I'm parsing in two. Okay, what's this gonna do which execution context it's gonna take us back in to, Alex?
>> Alec: Into the create flow [INAUDIBLE].

>> Will Sentance: Yeah exactly and it goes back in. Okay, where do we leave? We leave, we left with [LAUGH], we left messing with, we left with, we left with our, we left being rapidly kicked out of our function. [SOUND] Being kicked out and never getting a chance to try anything new now.

When we come back in, whatever we parse in as our input to next that takes us back into create flow is going to be the evaluated result of this last right-hand side work. It is gonna be our evaluated result of this statement here. This is gonna allow us to pass data back into our execution context, almost like an argument back into it.

And it's gonna be the evaluation result of this yield expression.
>> Will Sentance: So the yield 10 It's it did what it's job before it was to return out the ten. And then when we come back into the function, it didn't get a chance because this yield was so powerful it threw the ten out into the last call to next and [INAUDIBLE] element one.

When we come back in, that expression evaluates through it becomes two JavaScripts inside of here, whatever we pass in as the input. And that's the very nature of the design of these generator functions. That when you go back into them, you get to insert data back into their local execution context as the evaluated result of the previous yield expression, the previous yield statement.

That's not a thing which you can store in new num. But what we pass back into number two, which replaces that piece, is a thing we just throw a new num. So Abde, what gets sorted new num?
>> Abde: Two.
>> Will Sentance: Two, exactly. All right, and what's our next line of code say to do then, yield five plus what, Abde?

>> Abde: Newnum which is two.
>> Will Sentance: 2, which is to say yield 7 and where is that 7 gonna be thrown out to?
>> Abde: Element 2.
>> Will Sentance: Exactly, is the output of the next call is the 7 and that's gonna be stored in element 2. Well done, Abdi, excellent.

Look how much dynamic control we have over our function that gives us our element of our flow of data. And we say there's nothing like thinking about our data as flows because look how much control it now gives us. Now do you see how beautiful it is to have control over to think about data as flows of values, flows of elements?

Because we can literally control dynamically what the next element of that flow will be. It's a paradigm shift in how we think about designing our code. Okay we would run it again. What would we, Abde, if we were run return again, would we have in our element three?

>> Abde: We would have,-
>> Will Sentance: We did 5 + 2 for the previous one, what's the next one, Brian?
>> Brian: 6.
>> Will Sentance: 6, excellent, exactly, that very one on the bottom there you. We now have flows of data that we can switch on, when we call the next function, get the next element.

Switch on, call the next function, get the next element with flows of data that we get to control every element of them with actual code. It's very beautiful, all right. We got our thumbs on this, and the moment we are gonna discover that this pseudo pausing, let's just say how it's therefore stored.

We are therefore storing on the next call, our next function, we're storing our execution context before we return back into it, to resume it. It's not being, this is not staying on the I think. It doesn't make sense to, but we hold onto it with two pieces of information.

One, our backpack of data. Our backpack of data with num is 10, new num is 2, plus our position in the function, in the generator function, the line number and the positioning code. Which is, I think is stored squared bracket, square bracket, generator. It's called square root disorders.

>> Will Sentance: Square bracket, square bracket, generator location, square bracket, square bracket, and then whatever line is. Wait, suppose we were here,
>> Will Sentance: Line four, and that's all execution context is when it's not running. When you start running it, you take the thread to let that line fall and you make sure that's your local data.

So we start it. Put it on the column for that information, but that's all it is. So, what we're really doing when we pause, is we're saying hold on to the information. In the next, attached to the next that we then gonna go back to that we need to then be able to go back to and continue running that function.

And continue referencing that state locally that data locally. That's all it is, all right people? And then the next time we come out it was at line five and local data's changed slightly. Okay beautiful.

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