paint-brush
Understanding the Javascript Event Loop (Everything You Need to Know)by@sojinsamuel
4,840 reads
4,840 reads

Understanding the Javascript Event Loop (Everything You Need to Know)

by Sojin SamuelJuly 4th, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

The event loop is crucial for becoming a proficient JavaScript developer. JavaScript operates on a single thread, allowing it to process instructions one at a time. The call stack is an array where the JavaScript interpreter keeps track of the code you're running. You can spawn a [Web Worker] to run computations in parallel.
featured image - Understanding the Javascript Event Loop (Everything You Need to Know)
Sojin Samuel HackerNoon profile picture

Today, we will demystify the JavaScript event loop. All those fancy terms like Callstack and Callback queue may sound complicated, but don't worry! I recall how tough it was when I initially started learning about it three years ago.


I will guide you through the event loop in a way that I wish someone had guided me.


So, let's dive in and explore the following concepts together:

  1. Callstack queue
  2. Callback queue
  3. Event loop.


Understanding these concepts is crucial for becoming a proficient JavaScript developer, and by the end of this article, you will have a clear understanding of how they work together.


Are you ready?


Then click that ❤️ button on the right, and Let's get started.

Warm-up


While writing your day-to-day code, you may not actively think about the event loop, but it will likely be useful to understand at some point. It's also a commonly discussed topic in job interviews.

JavaScript operates on a single thread, allowing it to process instructions one at a time.

Hey, just a quick heads-up: you can actually spawn a Web Worker to run computations in parallel! It's pretty neat, but keep in mind that it has specific use cases, and there's one important thing to note: you won't be able to access the DOM from within the Web Worker. Just something to be aware of 🙂

Let's check out this code together! Can you take a guess at what the output will be?

console.log("A");

setTimeout(function() {
    console.log("B");
}, 0);

console.log("C");


What do you think the output will be?


Notice how I scheduled a timer with setTimeout, but I'm saying it should be called 0 milliseconds in the future.


Will the output be: A B C or A C B?


Take a guess 🤔


A, C, and then B are the answer.


Honestly, I don't expect you to get it right. It's a tricky piece of code, and that's why I'm here to help. In this post, I'll spend the whole time explaining how this code works.


So, let's dive in hacker 🦾

The Mighty Callstack


Think of the call stack as an array where the JavaScript interpreter keeps track of the code you're running. Let's consider the following code as an example:

console.log("A");

console.log("B");


Let's talk about the call stack:

  1. It begins empty.


  2. Next, the JavaScript interpreter adds the first line to the call stack. So, at this moment, the call stack holds console.log("A").


  3. The console.log("A") statement is executed, which prints "A" to the console. After that, this instruction is removed from the call stack, leaving it empty again.


  4. Then, console.log("B") is added to the call stack.


  5. This line is executed, resulting in "B" being logged to the console. Once again, this instruction is removed from the call stack, leaving it empty.


Check out this excellent tool below that visualizes the call stack loop (and more):

source: Latentflip.com/loupe

loupe - example 1 (It helps you visualize this)


Let's put the Web APIs and callback queue aside for a moment. Simply click on Save & run (I've already filled in the code for you), and take a peek at the Call stack.

Let's Look at an Example With a Function

Keep in mind that I won't use ES2015 since the loupe tool doesn't support it since it was written over 7 years ago. However, the concepts still apply to arrow functions.

function init() {
    console.log("A");
    console.log("B");
}

init();


Let me walk you through how the call stack is filled in this example:

  1. Initially, the call stack is empty.


  2. The function init() is defined but not called, so it doesn't go into the call stack. The call stack stores the instructions that need to be executed.


  3. When the execution encounters the call to init(), it is added to the call stack because it can be executed.


  4. At this point, init() remains in the call stack, and the JavaScript interpreter starts reading the code inside the function line by line.


  5. The line console.log("A") is added to the call stack. Now, the call stack contains both init() and console.log("A").


  6. The console.log("A") line is executed, which logs "A" to the console. Then, console.log("A") is removed from the call stack. So, the call stack now only contains init().


  7. The next line, console.log("B"), is executed. It logs "B" to the console, and then console.log("B") is removed from the call stack. Again, the call stack only contains init().


  8. Since there are no more lines to execute inside the init() function, init() is removed from the call stack, and the call stack becomes empty once again.

source: Latentflip.com/loupe


Let's take a moment to visualize the call stack using the same tool as before. Open the loupe, and check out example 2.

Enters Callback Queue


When I introduce setTimeout or other Web APIs, things can get a little trickier. These Web APIs work differently because of how JavaScript is designed and how it interacts with the visual part of the Browser (like the DOM, user interactions, network requests, and more).


So, instead of functions being executed immediately and added to the call stack, there is an intermediate step when using Web APIs like setTimeout. Let's see an example below:

setTimeout(function logA() {
    console.log("A");
}, 5_000);

I named the function logA so that when we visualize this example, it will appear as logA instead of being anonymous.

Here's how this code runs step by step:


  1. The call stack starts empty.


  2. Then the code setTimeout(function logA() {...}, 5_000); is added to the call stack.


  3. It gets executed, instructing the browser to create a timer for 5 seconds.


  4. The call stack becomes empty again (as the timer has been set).


  5. After approximately 5 seconds, the timer elapses, and the browser notifies JavaScript that it's time to call the logA function. However, the function cannot be directly added to the call stack.


  6. Instead, it is added to the callback queue, which is a separate "array" that stores callbacks from WebAPIs like setTimeout.


  7. Currently, the call stack is still empty, but the callback queue contains logA().


  8. Since the call stack is empty, it can process the next item in the callback queue (we'll explore this further in the next lesson).


  9. Therefore, logA() is moved from the callback queue to the call stack.


  10. logA() is executed, which adds console.log("A") to the call stack, executes it, and then removes it.


There's a bunch of stuff happening here, but don't worry, you can totally grasp it if you follow these two recommendations:


  1. Take a look at the visualization: loupe - example 3.


  2. Give the explanation above another read.


  3. Keep repeating steps 1 and 2 until everything becomes clear. And hey, if it's still not clear, just let me know in the comments. I'm here to help 😀

Why Did They Design It Like This?


The main reason for this is to improve performance.


JavaScript operates on a single thread, which means it has to handle several tasks simultaneously, such as running your code, rendering the screen (including CSS calculations and pixel drawing), and responding to user interactions like button clicks or page scrolling. All of these tasks happen on the same thread.


When the call stack becomes overloaded with a lot of tasks, it can cause your website to become unresponsive. To avoid this issue, the browser utilizes a scheduling system for Web API calls.


This system allows the browser to defer these tasks until the call stack is empty, ensuring that your website remains responsive.


However, it's important to note that the event loop, while effective in managing these tasks, is not a perfect design. It's still possible to inadvertently slow down your website due to the way JavaScript operates inside the browser.

Let's Talk About the Event Loop

Now that we understand the call stack and the callback queue, let's dive into the event loop, which is a straightforward part of the equation.


The event loop performs a task: it constantly checks if there are any items in the callback queue and sends them to the call stack, but only when the call stack is not occupied.


Take a look at this example: loupe - example 4.


Observe how the callback queue accumulates three instructions - logA, logB, and logC. Then, the event loop (depicted as an orange circle with arrows) forwards these tasks one by one, but only when the call stack is free.

Let's Revisit the Initial Example

console.log("A");
setTimeout(function logB() {
    console.log("B");
}, 0);
console.log("C");


The setTimeout call goes through the callback queue, so it runs after console.log("C"). That's why you'll see A, C, and then B in the console. Give it a try yourself on loupe - example 5.

The setTimeout

When you use setTimeout(..., 0), it won't actually happen instantly in 0 milliseconds. Instead, there will be a few milliseconds that pass before the code executes.


This delay occurs because the code needs to go through the browser to schedule a timer for 0 milliseconds, then it enters the callback queue and waits for the call stack to be empty before running.

JavaScript Video Highlights: Event Loop & Similar APIs

I want to share some great videos with you that I think you'll find really helpful and interesting.

One of the most-watched talks in the JavaScript community is Philip Roberts’ talk at JS Conf. It's a fantastic talk, and there's a good reason why so many people love it.


By the way, Philip Roberts is also the creator of the loupe tool that you've been using in this post.

Pretty cool, right?

I really think you should set aside some time to watch his 25-minute talk on the event loop titled What the heck is the event loop anyway? Believe me, this video will teach you so much. I still go back to it every now and then to refresh my knowledge.

And guess what? There's a newer version of this topic available, which covers other similar APIs. It's called In the Loop by Jake Archibald. I also recommend checking it out. I hope you enjoy these videos and find them valuable for your career 😃

Keep Exploring Hacker

You did an amazing job 🥳


I've worked hard to demystify the event loop and provide you with clear explanations and examples. Remember, learning about the event loop is an ongoing process. It's completely normal to face challenges along the way, so don't hesitate to ask me any questions.


Your feedback and concerns mean a lot to me, so please share them in the comments below.


If you found this article helpful, make sure to click the ❤️ button on the top right. Also, feel free to explore my other recent posts on HackerNoon (just scroll down a bit to find them).


Keep up the great work on your learning journey, and happy coding 👋