Check out a free preview of the full The Hard Parts of Asynchronous JavaScript course
The "Async 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 walks through an example asynchronous generators to showcase control over deferred functionality.
Transcript from the "Async Generators" Lesson
[00:00:00]
>> Will Sentance: Line 1. What are are we doing? Sean.
>> Sean: Declaring a new function due when data received.
>> Will Sentance: Due when data received. So this is my favorite page of code ever. This page of code is going to bring together everything we remember from our web browser features, everything from our new generator functions, our micro-task queue, our event loop, our crazy new generator functions, that include our persistent of state.
[00:00:35]
What you might call closure. All in one beautiful, complete setup. Very special. All right, good. Function one declared. Rick, next line.
>> Rick: You declare a generary function CreateFlow.
>> Will Sentance: Excellent! Hold on. There it is with the little star. Okay. Next line, Alec?
>> Alec: We declare a constant return next to element.
[00:01:07]
>> Will Sentance: Excellent, thank you, Alex, undefined for now. Here it goes, here it goes.
>> multiple: [LAUGH]
>> Will Sentance: All right
>> Will Sentance: [LAUGH] The diagramming, the mega diagram. I'm gonna put my call stack.
>> Will Sentance: I think there's room to do it here. I'm gonna put my full stack here. I think there's room.
[00:01:39]
There it is, there's my call stack.
>> Will Sentance: And I'm gonna put return next element is the output of calling createFlow, which is surprisingly, an object known as a generator object with a next method on it. And that's gonna be stored in what constant, Brian?
>> Brian: Return next element.
[00:02:12]
>> Will Sentance: Return next element, excellent, there it is, into return next element, with next is just a method. That's the method we're interested in. That's the method which when code should give us out a next value from our flow of data. Now,you probably already seen, we only got one use so we gonna have only one value.
[00:02:31]
But we could have many. But, only one here for now. Good, good. Next line is what, Ben?
>> Ben: Declare a constant future data.
>> Will Sentance: Yeah. Let's just make sure we're in the global execution context. Declare constant future data. There it is. This really does bring it all together.
[00:03:00]
So we're gonna be as precise as possible. Future Data which is the output of return next element .next call, which enters what execution context, Ben?
>> Ben: CreateFlow execution.
>> Will Sentance: CreateFlow execution context. There it is. In it goes. And by the way, notice how my execution context, now I don't close them.
[00:03:33]
Right? Because I'm not going to exit straight out of them. That's what I've done for both of them, I forgot to say. Okay, so return NextElement.next. Because next has this bond to createFlow,
>> Will Sentance: I start executing it. Into it I go, and what's the first thing that I declare in my local?
[00:03:59]
That's too wide. I declare in my local memory, Paul, what's the first thing I declare in my local memory.
>> Paul: Busted. I was taking notes.
>> multiple: [LAUGH]
>> Will Sentance: That's more dramatic then I expected. I'm calling createFlow, what's the first thing, my first body of, in the body of createFlow-
[00:04:17]
>> Paul: Dynamo.
>> Will Sentance: Exactly, the first thing I declare is data which is, again, I'm not correcting anyone's pronunciation.
>> multiple: [LAUGH]
>> Will Sentance: Which is undefined, right? Because we've gotta go to the right hand side. And the right hand side, of course, is a super powerful statement. It's one that's gonna actually kick us out of createFlow's execution context before we ever get to store anything in data.
[00:04:46]
Let's make it slightly wider. I don't want to squeeze this too much. Make it slightly wider. So data is going to be the evaluated result of the expression yield Fetch,
>> Will Sentance: Do I have space for this?
>> Will Sentance: [LAUGH] Yeah, there it is.
>> Will Sentance: And Fetch is a function that is a facade, for a whole, see that's when we're coming back together, for a whole bunch of, well, for a single major feature in the web browser.
[00:05:22]
But also, it does something in JavaScript as well, doesn't it? Let's do the bit it does JavaScript first. What does it do in JavaScript, Ben?
>> Ben: It creates a promise object.
>> Will Sentance: Exactly, so the Fetch statement returns which is going to be returned. No other data, no other data, no other data is going to be returned out the promise object which is going to be thrown, it's going to be thrown,unapologetically, out of that execution context.
[00:05:57]
To be the output of return next element.next, which is stored where, Victor?
>> Victor: Future data.
>> Will Sentance: Future data, excellent. And this promise object has what properties by default, Victor?
>> Victor: Value?
>> Will Sentance: Value, which is undefined, I don't have anything in it yet. It has status, which is pending, you don't have to worry about that.
[00:06:19]
And it has the hidden one, Unfulfilled, which is an empty array into which we're gonna put any functions we want to auto-trigger when value gets updated. Okay, and it's gonna be thrown out. When, it's gonna be yielded out
>> Will Sentance: This is where we're gonna pause, I guess. Because it's gonna be thrown out to be the output of returnNextElement.next so we returned out that promise object.
[00:06:59]
To make sure it's clear, it's a promise.
>> Will Sentance: To be the output of returnNextElement.next, which is then stored where, Ben?
>> Ben: In future data.
>> Will Sentance: In future data. So, let's put that over here. There's our promise object. Not very nicely drawn. There's our promise object with values undefined and unfulfilled is an empty array into which we're going to put functionality that we triggered when value gets updated.
[00:07:36]
And, of course, I hate saying of course. And that value is updated when Fetch''s background web browser API work Is complete and passes that value back into JavaScript. Okay, good. Be really, really, really clear with yourself, that we do not store in data, this promise object. We don't get a chance to store anything in data cuz the yield keyword is so strong.
[00:08:04]
Imagine like it's a return statement. You won't store anything, something assigned to return statement, you wouldn't even hit that assignment. The return just threw you out already. If through this expression, the result of it. Not the expression, the evaluated result of it, of its command. Which is the promise object out in the futureData.
[00:08:21]
As being the output of a next call which took us in here and the next call took us in here in the first place. And then we'd flow out in the futureData. But we're not finished with fetch yet. Fetch does a wealth of stuff in our web browser.
[00:08:40]
So here it is. Our web browser features. That is not good. Our web browser features. There it is. And what is the thing that fetch spins up in our web browser?
>> Blessing: XMLHttpRequest.
>> Will Sentance: XMLHttpReques, the XHPR feature. Look at this everyone it all comes back together what properties doesn't it need to know Blessing?
[00:09:19]
>> Blessing: The URL.
>> Will Sentance: Excellent.
>> Blessing: The path.
>> Will Sentance: The path which is that.
>> Blessing: And the type.
>> Will Sentance: Exactly. The method type which is get. And now that's going to be kicking off a request, an HTTP request to Twitter, there it is,
>> Will Sentance: Good. All right, is it complete yet?
[00:09:48]
Well at what time are we at? We're at something like here, I don't know, let's just say it's one millisecond, we're at one millisecond, is it complete yet, Blessing, at 1 millisecond?
>> Blessing: No.
>> Will Sentance: No. He's going to go and get the data. It's going to take a bit of time to go and get that data.
[00:10:03]
On completion, Blessing, what do we want to update? What value do we want to update back in Java Script?
>> Blessing: futureData.value.
>> Will Sentance: futureData.value excellent is gonna be the returned data. Perfect, now we have finished fetch's work. We yielded out the object. Remember, return next element dot next did nothing particularly interesting with next.
[00:10:34]
All it did was kick off createFlow's execution context. We went inside, we set up data but we didn't even assign anything to it. Because the right hand side said go fetch, which returned out a promise but then it said yield. Meaning throw the return value of this right hand side bit out to be the output of the returnNextElement.next that took us into createFlow.
[00:10:54]
And that output, I'm gonna put it here, it needs to be really clear. That output object,
>> Will Sentance: Got stored in computer data, excellent. Okay, we're now finished all the fetch work. We've yielded back out, back into global execution context. The last line in global as futureData is the thing that kicked off createFlow with back out and the next line of the global execution context is what, Abdee?
[00:11:27]
>> Abdi: futureData.then.
>> Will Sentance: Excellent, very good, man. futureData.then to which we're passing the entire display function definition. Do we remember what that does, Abdee, to the futureData object? Do you remember what then does with the display?
>> Abdi: Yeah, we're passing in the unfulfillment.
>> Will Sentance: Exactly.
>> Abdi: I think.
>> Will Sentance: Exactly, did I switch it from unfulfillment to unfulfilled?
[00:12:03]
>> Abdi: Yes.
>> Will Sentance: It's a hidden property, it's unfulfillment.
>> Brian: This function's also called doWhenDataReceived, not display.
>> Will Sentance: Shit okay, all right we're losing it, we're losing it okay, good, thank you James. And it's unfulillment is what it says in the actual spec if I remember right. I mean, we don't get to see this.
[00:12:22]
We can't even access these properties, so I guess it's not the end of the world what it is. We just know it's there. But we can't even access it. We can't console log it. Yeah, unfulfilment. I think it's unfulfilled. Okay, whatever.
>> multiple: [LAUGH]
>> Will Sentance: Maybe it's on fulfillment.
[00:12:41]
Anyway, it doesn't matter because we can't even access it. We just know it is the collection of functions that will be auto triggered. And exactly we're passing doWhenDataReceived, not displayed. Sorry, doWhenDataReceived. Are we calling that function here, James?
>> James: No.
>> Will Sentance: No, what are we doing with it?
[00:13:01]
>> James: Adding it to the unfulfillment list.
>> Will Sentance: Hooray, exactly. List, exactly right. There it is, due when, that's all that we're interested in, dot then doing, passing that function to the unfulfillment collection of functions which will be auto triggered when value gets updated. All right and to be continue on.
[00:13:24]
We continue, we continue on now. And our global execution context work is done. So, we set up everything. We have in JavaScript a promise object with a function that's ready to be triggered when the value gets updated. Let's take a little preview at that function, doWhenDataReceived. Look what it's running.
[00:13:51]
It's running something that's going to take us back in to continue running our code inside of the createFlow function.
>> Will Sentance: First time ever we've been able to write pseudo synchronous ordered code which we exit from and come back into. We're going to trigger it's going back into it using this .next method.
[00:14:10]
Going back into createFlow. So we set that up to happen in the web browser itself. We set up the background task to go and speak to the Internet. Fetch did that for us. We exited out of this execution context to createFlow that we got into using .next. We exited out of it.
[00:14:30]
We attached the function to the promise object that we passed out to futureData. Knowing that when our background work completes, value of futureData is going to update it, it's gonna also trigger doWhenDataReceived. Which hopefully has written in it the functionality to take us back in to this execution context with our value from the background work as an input that this yield statement will evaluate to, just like we did before.
[00:15:02]
We're going to pass the value in and the last yield expression will evaluate to whatever we pass back in the execution context. Crazy. Crazy, crazy, crazy. All right, here we go. Time's gone by in the background, 200 milliseconds have gone by and our data is on its way back.
[00:15:22]
So Blessing, at 201 milliseconds is our background work complete?
>> Blessing: Yes.
>> Will Sentance: And we brought back our dear API response. Which is high and Blessing, what are we gonna update with that high?
>> Blessing: futureData.value.
>> Will Sentance: futureData.value, excellent. There it is, which is gonna auto trigger. Well, let's be complete about this.
[00:15:49]
It's gonna trigger the unfulfilment array of functions to be called, but we don't get to call them straightaway. So instead, it's going to do what, Blessing?
>> Blessing: It's going to get into the microtask queue.
>> Will Sentance: Into the microtask Q. There it is, and there's the event loop. Into the microtask queue, do when data received goes.
[00:16:14]
Do when data received is past the microtask queue at about, let's just say about 201 milliseconds. At which point we check with, it was something like two milliseconds that we've finished our synchronous code executing. Time's past, and we're now at 201 milliseconds. Is there anything in our call stack at this point blessing?
[00:16:44]
No, it's all done, there's no global code left to run. So the event loop goes, hurray! You're clear to add stuff from the microtask queue. Which we do. We add do when data received. And what's that input to it, Blessing? What's that input to do when data received?
[00:17:07]
>> Blessing: The value, which is high.
>> Will Sentance: The value, which is high, because the value being filled in and the status going to resolved. They're three statuses, but they have multiple different names for them in different parts of the spec. This function is going to be triggered with the value as its input.
[00:17:26]
And so, we pass the value high as the input, and at 201 milliseconds, we call do when data received with the value of high. We create an execution context for it. What's the first thing in its local memory? Blessing.
>> Blessing: Value.
>> Will Sentance: Which is set to?
>> Blessing: Which is set to the argument high.
[00:17:55]
>> Will Sentance: To the argument high. Excellent. And what's the first line, Paul, in the body of this function say to do?
>> Paul: It says call the property. [CROSSTALK] Next on the generator object return next to it.
>> Will Sentance: Absolutely, with what value path to it?
>> Paul: The string pi value.
[00:18:20]
>> Will Sentance: Excellent. And what does this next do? It takes us back into create values execution context, at the position that it left out, it exited with a yield statement. And the thing we pass to it is the evaluated result, that is the right-hand side never got to be evaluated to a value, to an actual thing, to be stored on the left-hand side of data.
[00:18:44]
We were booted out before we got to store anything in data. But now we come back in. Whatever we pass into next is what the last yield statement on the right-hand side will evaluate to. And so we get to evaluate it to high. Add data that came back from the Internet.
[00:19:01]
Pass to value, pass to the due and data received, pass to the next inside of there, pass back to be the evaluated results of the yield expression. My goodness, and we're gonna store that, Blessing, in what constant inside of Create Flow?
>> Blessing: Data.
>> Will Sentance: Exactly. And there it is.
[00:19:21]
And then we get to continue in our pseudosynchronous execution context thread that continues through our create flow, and what's the next line we hit, Ben?
>> Ben: It's a line about data. And it's the high.
>> Will Sentance: It's the high. And look at that, people. I genuinely think this is absolutely beautiful.
[00:19:41]
For the first time
>> Will Sentance: In history we get to exit a function, we get to show code that I want to do to set off a long term task. Then exit out of the function, continue with synchronous code and then when that long-term task value comes back, trigger, it's going back into the function, and continuing within that same function block.
[00:20:09]
Now we get to think of our code that's deferred, the console log, to be run until we get data filled in with an actual value. We get to write it immediately below, in logical steps, the code that kicked that work off. A first time ever. All right, so this is profoundly hard, and you can see it brought everything we've done together, but let's have thumbs.
[00:20:39]
In a moment we're gonna clean all of this up. We're going to auto trigger not a function here, we're going to instead auto trigger the next block of code in here. We're not going to trigger a function that then triggers us going back in here. We're instead just gonna trigger straight from here, continuing our code inside the function.
[00:20:59]
Using a new way of defining our generated functions, called async/await.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops