Beginner's Guide to Understand Javascript's Callstack, Event Loop, and Event Queue
While I was learning about Javascript, my favorite part was learning how it processes your code, I found it somewhat difficult to understand at first, but once I got it, it blew my mind. In this blog, I hope to explain how JavaScript handles your instructions (your code) in a way that makes it easy to understand. We will be talking about the call stack, the event loop, and the event queue and how they work together to execute your code.
So let's get started. Let's pretend you're a supervisor (the programmer) and you have a manager (the JavaScript engine) and 3 employees, Mark (function one), Sally (function two), and Will (function three). You (the programmer) have assigned Mark, Sally, and Will different tasks to get done. Now their task is to present different information. The manager (JavaScript) can only listen to one person at a time so Mark (function one) comes into the room (the call stack), presents his information and then leaves (function one is removed from the call stack). Then Sally (function two) comes in (the call stack) presents her information and leaves (function two is removed from the call stack). Lastly, Will (function three) comes in (the call stack), presents his information and leaves (function three is removed from the call stack). This process of having one person (function) come into the room(the call stack) at a time is a synchronous process. Each function runs one at a time.
Now let's talk about when a function is called inside another function. Say Mark (function one) has been given some tasks to present but he needs information from Sally (function two) before he can continue presenting his information. Mark gets called into the room (the call stack) first, at some point, he can't continue until Sally(function two) presents her information, so Sally comes into the room (the call stack) and while she is presenting her information, Mark stays in the room (the call stack)and waits for Sally to finish. Once she is done, Sally leaves the room and Mark uses the information Sally presented to finish up his presentation and then leaves (the call stack). This is how JavaScript handles nest functions. Again, this is a synchronous process. The more nested functions there are, the more they get stacked in the call stack until each one is done with its tasks.
Now this is where it gets interesting! JavaScript can run code asynchronously. It has asynchronous functions built into it, like the fetch function, setTimeOut function, setInterval function, event handlers, or any time you use the async/await. These asynchronous functions are handled differently by JavaScript. Let's go back to our analogy.
As Mark (function one) is waiting for Sally (nested function two) to finish presenting her information, Sally wants to give you some snacks so she calls Will, (Will is now an asynchronous function). Will comes into the room (the call stack) and promises to provide snacks (data) but it'll take him a hot minute to come back with those yummy treats, so while he goes to the store (the event loop) to buy some snacks (fetch or process some data), Sally (function two) continues to present some information with the promise that when she and Mark are done with their presentations there will be snacks (the data) at the end. When Will comes back with the snacks, instead of just coming in and interrupting the presentation he waits in the lobby (the event queue) until Mark and Sally are done. Once they are done, the manager (JavaScript) checks the lobby (the event queue) and sees that Will is back with some snacks (some data). The manager brings Will into the room (the call stack) and provides the snacks (the data) to the supervisor(you, the programmer).
This is how JavaScript handles asynchronous functions. When an asynchronous function is called, it needs some time to fetch or process whatever data you want it to handle. Instead of stopping the rest of your code from running (having everyone wait for Will to come back), it sends the asynchronous function to the event loop and continues running your code until the call stack is clear. Once the asynchronous function is resolved, the event loop puts the results in the event queue. Once the call stack is clear, javascript then checks the event queue for the results of the asynchronous function and brings it back to the call stack to provide you with the results. One thing to note is that the event loop periodically checks the call stack for any asynchronous functions that need to be handled.
So to wrap this up, JavaScript runs your code synchronously. It can only handle one thing at a time but there will be instances when you want the user to continue using your application with no interruptions. So this is when you'll want to use asynchronous functions. When an asynchronous function is called, it immediately returns a promise, promising you some results once the function has been resolved. It then places the asynchronous function in the loop event and when the results come back, it gets placed in the event queue. Once the call stack is clear, JavaScript checks the event queue and moves the results to the call stack.
Learning how JavaScript works under the hood can help you tremendously when writing your code. Having this understanding coupled with knowing how to use the debugger will help fix/find bugs much easier. To see Javascript execute your code in real time you can go to this site and play around with the code you write. This is a simple overview of JavaScript's call stack, event loop, and event queue. More things happen underhood but we'll save that for another blog. Don't be afraid to dive into it, it'll blow your mind and make it worthwhile.