Lesson Description

The "Callback Queue" Lesson is part of the full, JavaScript: The Hard Parts, v3 course featured in this preview video. Here's what you'd learn in this lesson:

Will highlights that even a zero-millisecond timer does not immediately execute the callback function; instead, it is placed in a queue and waits until all global code has finished running. This queue is called the Callback Queue.

Preview

Transcript from the "Callback Queue" Lesson

[00:00:00]
>> Will Sentance: We're interacting with the world outside of JavaScript now, so we're going to need some rules. So to do so, we're going to walk through this code where we declare our print hello, we're going to have a function that I'm not going to write the body of, and actually, for folk who do go and experiment with this code, it is very, very important that this block for one sec is doing something that takes a significant amount of time within our single thread of JavaScript.

[00:00:26]
What could we use? Chris, what could we use to take a significant amount of time within a single thread, maybe block for a second. A for loop, a for loop. But by the way, people, that for loop's going to need to be like, you know, doing a lot of stuff. They're very optimized engines at this point, so your for loop's going to really need to be clever to even take a second. But you can come up with something that could take a second natively.

[00:00:47]
Maybe it's some sort of mathematics, you're doing the for loop, something recursive, but it's going to take some real computationally taxing stuff at this point to block for a minute, for a full second, but that's what that function's going to do. We're then going, so we define those two, we're going to first actual execution, run, set, timeout, and this time, we're going to pass in 0 milliseconds.

[00:01:12]
So Tenzin's question might very soon be answered. We're then going to call that block for 1 second within JavaScript land. It's going to block within JavaScript for a full second and presumably, Print hello is just going to bounce back onto the call stack as soon as it's ready within that block for one sec. That's my, that is my, that is my no voice, isn't it? Alright, good. So let's walk through this code line by line, people, and it should hopefully illuminate that we must have some additional rules or structures to deal with speaking to an environment, speaking to stuff outside of our very crisp, predictable environment.

[00:01:56]
In fact, we do want to recognize that that's our underlying goal in programming, even if we have, when we're dealing with an outside world, things get unpredictable, right? You make a request to the network, it may take 10 milliseconds, 100 milliseconds, it may never respond, it may respond with some other information. So many different things can go on. That is very, very different to our pure JavaScript, where unless something's broken, it is going to do every single line one after another until it's finished doing those lines.

[00:02:33]
Everything is at least incredible and we do want predictability when we're writing code, so we're going to see that predictability goal is so important that the rules we have for code coming back in and being, or sorry, stuff outside of the browser having an influence back in, sorry, stuff outside of JavaScript having an influence back into JavaScript. We're going to need some pretty prescriptive rules, we're going to need something that might even seem a bit overly prescriptive, but at least it means we can always predict ultimately the order of our code execution.

[00:03:15]
And as always, we start off in our JavaScript land. And we have our global memory here, which is going to be drawn with a straight line, for once, there we go. But now, and we have our call stack. Here it is. Which is going to keep track of what JavaScript code we're running, then we're going to have our thread of execution. But now, people, what, Chris, do I add that I didn't have before? What's my new part of my code below and outside of JavaScript land going to be called?

[00:04:00]
The web browser features, or we tend to call them the web browser APIs because we access them, we interface with them from within JavaScript. The I stands for interface. OK, good, so into our code we go, and the first thing we're going to declare, Joe, you're up, give me my first two function declarations here. We're going to define a function called print hello. Yep. And then we are going to define a function definition for a function called block for one sec.

[00:04:33]
Block for one sec, which we are not going to write out the body of that code, so to be really clear, people, we're going to need to actually add some code there if we were running this directly, but we don't need to fit it on the screen here, but it would be doing something like a loop. Next we hit our facade function call, or if you want to be correct and fancy about it, we hit our call to a web browser API or web browser feature, this one being via setTimeout, therefore this one being what, Joe?

[00:05:08]
Timer. The timer. And we're going to pass to it, what, Joe? Two arguments. The first, the function definition, print hello. Beautiful, the function definition, right? Are we calling print hello at this moment? No, we are 100% not, exactly. We're just passing the definition and the second, the value of 0. Yes, which represents, because we know this in looking at setTimeout docs. Yeah, sorry, I talked over, you say that again, sorry, Joe.

[00:05:35]
Yeah, 0 milliseconds, Joe said 0 milliseconds exactly. SetTimeout, Michael. Does it have consequences in JavaScript that we're interested in? No, where does it have consequences? In the browser. In the browser. And so I'm going to do my little Chrome logo, forgive me for that, no favoritism here, and turn on a timer down here in the web browser with the all-important information being, also, by the way, did we notice how nice and straight Michael's line was and how nicely drawn the arrowhead was?

[00:06:13]
Yeah, I know, what a way to show up my, it is perhaps misguided for someone who has at best flawed handwriting, at worst terrible handwriting, to be so committed to a blackboard. Never mind, so our 0 milliseconds gets thrown into our background work in the web browser, where we're doing, we turn on our timer and set it to 0 milliseconds. We know that when we're finished, we can say, and let's just say on completion, but you know, when we're finished, we are going to want to run what function, Joe?

[00:06:50]
Sorry, block for one sec. What? When we're finished running our timer, on the timer for what's the function we passed into the timer? Print hello. Print hello, perfect. Excellent, there it is, the print hello function definition. What did I forget? Time is passing in this world now, so at 0 milliseconds, we have, well, here's the question. Almost a trick question. Tenzin at 0 milliseconds, is our 0 millisecond timer complete?

[00:07:21]
Not yet. To be fair, I've noticed every time I ask this, people think no, but at 0 milliseconds, is a 0 millisecond timer complete? I guess so. Yeah, I guess so, exactly, it's complete. It is absolutely complete. So presumably, our print hello function gets thrown right back onto our call stack, it's ready to go, it's ready to run. And yet, it certainly does not. Let's just make sure we have our console in here as well.

[00:07:53]
It certainly does not. What the heck is happening? Our print hello function is ready to run, it should be added straight back on the call stack, right? It's a 0 millisecond timer, it's complete. Exactly as Tenzin said, it is complete in the background. OK, I'm not going to beat around the bush because why would I? It turns out, people, we have another feature that we need for this interaction between an outside world and the JavaScript engine that sits as an interface between the two.

[00:08:39]
It is going to be something called the callback or task queue, and it is into there that our print hello function goes at 0 milliseconds. At 0 milliseconds, even though it was immediately set up to run after 0 milliseconds, it cannot come back on the call stack, we'll see why in a moment. Instead it's going to be added here, print hello, it's going to go and sit and wait in this queue. Sit and wait in this queue for, well, we'll see what in a moment, and we though, for now, plow straight on and at 1 millisecond, and we're sort of just being ordinal here, we're just talking about the order of 0, 1, 1's after, not per se, actually a millisecond later, it'll be much less than that.

[00:09:30]
We're going to hit the call to what function, Chris? Block for 1 second. Block for 1 sec and we're going to execute it, and we're going to create, we're going to add it to the call stack, block for one sec. And we're going to create everyone together, a brand new execution context. Into it our thread goes. Maybe I should do the thread in green, no, we'll do the thread in, 'cause that's time, isn't it?

[00:09:55]
We'll do the thread in white, 'cause it's a JavaScript thread, and into it we go where we are going to run for 1000 milliseconds some sort of for loop. We don't know what it's going to do, but it's doing it very much in JavaScript, which means it can't be a very long task because JavaScript literally doesn't let us do any very long tasks directly in JavaScript. It has to be lots of tiny tasks, meaning like a for loop.

[00:10:20]
So like doing lots and lots of things very, very many times. That's what we're doing inside of here, maybe some sort of loop. And I guess at any moment in that, our print hello is going, excuse me, excuse me, you know, you're doing a, can I come back on? Do we think that our print hello is allowed back on the call stack during that block for one sec function call? Absolutely not. After 1000 milliseconds, we are now back out into global at 1,001 milliseconds, and so we are going to pop block for 1 second off the call stack.

[00:11:20]
Off it comes, off it pops, and we're back in the global. Now, presumably print hello is allowed back on the call stack. What do people think? No, no. No, exactly. Instead at 1,001 milliseconds, what? Chris is going to be executed. Console, console log. Me first, and there at 1,001 milliseconds. We get the me first in our console. And now finally, at 1,002 milliseconds, what finally will be added back to our call stack?

[00:12:09]
Tenzin, what do you think? It's the print hello. It's the print hello. It gets dequeued and gets added to our call stack. Brand new execution context for it. There it is, call it, and what does it do at 1,002 milliseconds? Oh, this is when I start to get tired from running around the board and changing my, it console logs. Hello. Oh my goodness. We have perhaps the strictest rule. What do we think our rule is?

[00:12:39]
Yeah, Joe? Callback queue doesn't run until the end of the global queue is finished. Until all of our global, not just until stuff on the call stack is finished executing, definitely that, but until all of our global code is finished running. And I'll be honest, I almost, when I first did this, I almost couldn't believe, you know, you could have a million console logs, and still, everything would be left until after.

[00:00:00]
But it comes back to this fundamental idea that we want predictability. And if predictability is all global code must be done before we ever run anything out of the callback queue, at least that means we can be confident that all global code will be run, and at no point might something jump back in. It is a strict rule, but it is a fundamental rule.

Learn Straight from the Experts Who Shape the Modern Web

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