Node Event Loop
This article is meant to help you understand how the Event Loop works.
Understanding what's Happening in Chrome.
Take a look at this image (credit: Philip Roberts): Now this image might be a little confusing at first, but bear with me here. Let's break down this image real quick.
-
V8 Runtime: The first thing we have is the box in the top left with JS on it that contains a "heap" and a "stack".
- Heap: How memory allocation works
- Stack: The call stack I'll be talking about in in the next section.
-
WebAPIs: Web APIs are things that are provided by the browser, that the runtime doesn't provide. As you can see, some things that the web APIs provide us are:
- DOM/Window
- ajax (XMLHttpRequest)
- setTimeout
-
Callback Queue: When things happen asynchronously, it will be put in the callback queue as it finishes.
-
Event Loop: How things are re-pushed back onto the stack.
If the Callback Queue and the Event Loop don't make sense, just keep reading. I'll be explaining more about them as I move forward.
Call Stack
The first part of understanding the Node Event loops is to know what the call stack is. (That's the stack inside V8 runtime in the first picture).
Something important to note is that Javascript is a "single-threaded" programming language. Which it means it has a single callstack. Which means it can do one thing at a time.
The call stack is a Last-In, First-Out data structure. Things that get put on the stack try to resolve one at a time. Imagine a stack of papers, whenever you put a new piece of paper on top, the call stack resolves things from the top down.
If this doesn't make much sense, just bear with me for a second, I promise it will make sense when you see the picture.
Take a look at this code.
let main = () => {
methodA();
}
let methodA = () => {
methodB();
}
let methodB = () => {
methodC();
}
let methodC = () => {
console.log('success!');
}
main();
Again with the stack of paper analogy: normally what happens is that in an empty stack, a piece of paper is put on, and then removed immediately. It gets more complicated when the paper (acting as a function) calls another function (another sheet of paper). So what happens is the first sheet of paper is still there, but another sheet of paper is put on top of it. The top paper resolves, and then the bottom paper resolves. If it still doesn't make sense, just keep following along and it will make sense when you see the picture.
In the code snippet above, what is happening is we're calling main
on the last line of the code snippet. Main calls methodA
, but because main
hasn't finished yet, it's still on the stack. Next, methodA
calls methodB
and the same thing happens; it gets pushed onto the stack. Lastly, methodC
is called and put on the stack next. Because none of these have actually finished yet, everything is still on the stack. When the console.log
of methodC
happens, the stack will begin to resolve from top -> down.
We can visualize this with the image below. (Credit: ajbraus)
By visualizing the call stack, it also helps us understand what is happening and where things are being returned out of the call stack.
let multiply = (x, y) => {
return x * y;
}; let printSquare = x => {
let s = multiply(x, x);
console.log(5);
}; printSquare(5);
printSquare
is called with an argument of5
and is put on the stack.multiply
is called with argument of also5
and is put on the stack.multiply
is evaluated and returns25
.multiply
is removed from the stack and is assigned to variables
console.log(s)
is next in line and is put on the stack. After25
is logged, theconsole.log
is removed from the stack.- With nothing left in the function,
printSquare(5)
finishes and is also removed from the stack. - Tadaa! Empty stack.
(Credit: Alexander Zlatkov)
Synchronous/Blocking Code So what is "blocking code"? There really isn't a set definition of what "blocking" code is, it's really just "slow" code. Things that are slow include: recursion, image processing, data fetching, etc.
Imagine the call stack again, and imagine this code.
let first = $.getSync('http://www.first.com');
let second = $.getSync('http://www.second.com');
let third = $.getSync('http://www.third.com');
console.log(first);
console.log(second);
console.log(third);
Knowing what we know from the callstack, the synchronous call for first would be put on the stack, when that call finishes, second would be put on, etc, etc.
After that the console.log would log the first, second, and third in that order. Makes it pretty simple to understand.
So what's the problem?
We're running this stuff in the browser. While the slow/synchronous stuff is happening, the browser can't do anything. You can't click things, the buttons don't re-render, nothing can happen until the slow things finish.
Solution: Asynchronous Callbacks
Basically, we run some code, give it a callback and when it's done, the callback will run.
console.log('hi');
setTimeout(() => {
console.log('my name is');
}, 5000);
console.log('wilson');
If you have any experience using asynchronous callbacks, you'll know what happens here. The first console.log happens, the setTimeout happens, the last console.log happens, and 5 seconds later, the console.log from the setTimeout magically reappears and logs itself.
Output:
hi
wilson
my name is
How this actually works (with the call stack) is this:
- console.log('hi') is put on the stack and resolves.
- setTimeout is called.
- but where does the console.log go?
- it doesn't go on the stack
- we have no way of describing it yet, but it kinda just disappears.
- console.log('wilson') is put on the stack and resolves.
- console.log('my name is') magically shows back up and resolves.
Event Loop
We finally get to talk about what you're really here for: the Event Loop.
While Javascript, the runtime, can only be doing one thing at a time, there are ways we can actually do more than one thing at a time. Refer back to the first picture in the blog.
Our browser is more than just the Javascript runtime, we have these Web APIs that let us do other things (like slow AJAX calls).
If we take this code (basically what was above):
console.log('Hi');
setTimeout(function cb() {
console.log('there');
}, 5000);
console.log('JSConfEU');
We can properly visualize what is actually happening here.
(Credit: Philip Roberts) This is taken from his talk at JSConfEU so if there's pauses, its because he's talking. (Also there's no sound. I'll link the video at the end though.)
So What?
If you've done any asynchronous work at all, you've probably heard someone say something like "oh yeah, you need to setTimeout 0 on it." And (hopefully) all the information above, let's us see what that setTimeout actually does in the browser. And (hopefully) this will help you understand what is happening under the hood in this Javascript Event Loop!