Transcript from the "Callback Queue" Lesson
[00:00:33] When would my print hello jump back in and start running? I need a way of predictively figuring that out, right? Or what if I had a function that blocked the thread here for a while, maybe a whole second? It's got inside of it a while loop or a for loop, something really computationally taxing.
[00:00:54] This function blocked for one second, blocks when it's called the whole main thread for one second. What if that happens, when does this print hello function get to come back to the call stack?
[00:01:20] All right, we're gonna walk through this all. Precisely line by line and see exactly how this is going to play out. So we will start walking through this, lovely. All right, line one, what is happening in line one of our code?
>> Ott: We are declaring function print.
>> Will: Really slowly for me, because obviously I'm getting ahead of myself here.
>> Ott: We're gonna declare print hello.
>> Will: Perfect, there it is, print hello is in that global memory. Excellent, thank you Ott, what's the next line, tell us.
>> Ott: We're gonna declare block for one second.
>> Will: Declare block for one second, and we're not gonna figure out how that's implemented.
[00:02:08] But the way you would implement it would just be something like a while loop or a for loop. That takes, for each value of i and appends something to an array. That just starts taking time, and so that could block the thread, if it were run for a bunch of time.
>> Andrea: So it creates a new execution context?
>> Will: Not in the conventional sense.
[00:03:34] It instead speaks to-
>> Andrea: The web browser API.
>> Will: The web browser APIs, exactly. Our web browser APIs, otherwise known as features of the web browser. And it says, create, so web browser API, which one? Andrea, which one's it gonna say to create?
>> Andrea: setTimeout.
>> Will: Yeah, well, so it's gonna create a timer.
>> Andrea: Timer set.
>> Andrea: One second, sorry, zero seconds.
>> Will: Zero seconds, exactly, zero milliseconds, and associated with it is our?
>> Andrea: Our entire function.
>> Will: Exactly, our entire function definition, and that's zero. So there's the zero milliseconds, and then here is our reference to our print hello function, and is it complete? Well, it's gonna be complete in a second, but for now, it's not complete.
[00:04:50] But it's gonna be complete instantaneously, so in a sense, I guess it's instantaneously complete.
>> Will: What happens at this point, we'll set timeout, has done its work, it's spoken to the web browser APIs. And we then hit what line of code, Katie?
>> Andrea: We execute block for one second?
>> Will: We execute block a second, we push it to the call stack.
>> Andrea: Create a new execution context.
>> Will: Block for one second, exactly, a new execution context, here it is. Block for one second, a new execution context. Now we're not gonna say what it does. We just know sort of while loop, or just something that just takes a second before.
[00:05:44] So we're gonna use green to just show it's plus 1,000 milliseconds is how long it's gonna take before we go back to global. Before this execution context closes, I just have thumbs on that notion. I don't wanna assume that people understand what we're doing there. We're doing something here that just happens to take time.
[00:06:26] Let's have thumbs on that, don't know what that means, clarification, I get that notion, okay, everyone gets that, good. All right, so it's taking 1000 milliseconds before we exit our global execution context, we go back into our global execution context. But hold on, during that time, block for one second is on our call stack, we ain't going back to global.
[00:06:49] But during that time, our timer has finished, right, it's finished after what, zero milliseconds. So our time is finished at, it's complete at, let's say one millisecond. Let's be generous and say one millisecond from when it was first sent to be set up, it's completed itself. So what do we say when our timer completes?
[00:07:09] Dave, what shall we do when our timer completes? What should we do in theory, Lindsay?
>> Lindsay: We should call the print hello-
>> Will: Exactly, we should call the print hello function. So let's imagine we did that, are we going to push it on top of our call stack?
[00:07:47] That doesn't seem like a good approach, I don't think. So what are we gonna do? Actually, let's definitely, immediately erase this, we're definitely gonna do that. So what could we do as a reasonable, so Lindsay's suggestion was, we wanna call that print hello function on completion, we push it on top of the call stack.
[00:08:12] But we know that that's not gonna make sense cuz we're, right now, only one millisecond into running block for one second, and who knows when this will finish? Well, we know that it's gonna finish in 1,000 milliseconds, we can't just suddenly start executing print hello, so we've got a conundrum.
[00:09:07] This was Katie's question as well, how do I get these back? There's two other pieces to explain it.
>> Will: One is called the callback, message, or task queue. We can call any three of those, we'll call it the callback queue, they all mean the same thing. The callback queue, we're gonna put it over here.
>> Will: So it may seem fairly reasonable to say.
[00:09:58] You can't insert and start running immediately, that's just not gonna work. So what if instead, we had a queue? A queue into which we pass this function when it completes. There it is, the function definition is added to a queue. And that queue is waiting for some condition to take place on this call stack.
[00:10:22] And what would be a useful way of saying, okay, now I feel it's safe to run this print hello function? Arturo, what would be a sort of reasonable rule? Here's my print hello function, I know I can't run it yet because I've still got, I'm in the middle of running block for one second.
>> Ott: You have to be on the global call stack.
>> Will: I have to be in global, that might be enough. But what if I'm in global and I've still got a console log ready to go next? Actually, the fundamental rule is, I will not add anything from my callback queue that came in from my web browser APIs.
[00:11:23] Let's see that, I need to finish running block for one second, and at 1,000 milliseconds later, I'm gonna go back to global. And what am I going to do in global first, what it's say I'm gonna do in global first?
>> Ott: You're gonna console.log.
>> Will: I'm gonna console.log.
>> Will: Me first, me first.
>> Will: There it is, I've blocked for one second. I'm now at about, I think it's something like 1,001 milliseconds. There it is, where I console logged me first, and block for one second is already off the call stack at this point. I do my console log me first, and then at this point, I can add my call to print hello at last, I can add it to the call stack.
>> Will: It's on top of the call stack, and so if I'm calling it, Lindsay, what happens? What do I do when I start calling a function?
>> Lindsay: Create a new execution context.
>> Lindsay: Console log hello.
>> Will: Hello, and let's just write in our timings of this. So me first happened, I don't know, in 2 milliseconds, and hello happened at, my goodness, 1,002 milliseconds. But we said that our timer for our set timeout should only take 0 milliseconds. We set our timer to complete after 0 milliseconds, but it didn't end up running the callback function that was passed to set timer until 1,002 milliseconds.
[00:13:40] Sorry, that's a mistake, our me first was also blocked by our blocking function, our me first is also blocked.
>> Will: We had to wait all that time, and so this tells us that our asynchronous world takes a second sort of status to all other stuff happening in our synchronous land.
[00:14:36] So all our set timeout can tell us is, at a very minimum, our set timeout will delay the running of that function for 0 milliseconds, or 10 milliseconds, or 100, or 1,000, whatever it might be, at a very minimum. But only at a minimum, and it could end up being as long as, for example, 1,002 milliseconds.