📋 Table of Contents

  1. Introduction
  2. Step-by-Step Explanation of the JavaScript Event Loop Execution
  3. Data Structures: Call Stack and Event Queue
  4. Key Takeaways

🔗 Live Demo: Click Here

🎥 Demo Video:
Watch the video

Introduction

JavaScript is a single-threaded language, meaning it can only execute one task at a time on the main thread. However, this doesn't mean JavaScript can't handle multiple tasks simultaneously. The key to understanding this behavior lies in the Event Loop.

The Event Loop allows JavaScript to perform asynchronous operations, such as waiting for a network request or timer, without blocking the main thread. By offloading tasks to be executed later, JavaScript creates the illusion of parallel execution while still maintaining its single-threaded nature.

In this article, we’ll take a closer look at how the Event Loop manages both synchronous and asynchronous tasks. We’ll walk through how JavaScript handles operations, manages callbacks, and uses the Event Loop to ensure that asynchronous tasks don't block the execution of synchronous code.

To help visualize this process, we provide a series of visualizations that illustrate each step of how JavaScript handles tasks. These visualizations are based on a specific code example, which is shown throughout the images to simplify the explanation. Each visualization is accompanied by a description to clarify what's happening at each step. This approach helps us see how tasks move through the Call Stack, the Event Queue, and how the Event Loop orchestrates everything.

Data Structures: Call Stack and Event Queue

In this article, we’ll also briefly touch on the two main data structures that help JavaScript manage synchronous and asynchronous tasks:

🍽️🍽️🍽️ Call Stack: The Call Stack is where JavaScript keeps track of synchronous functions that are executing. It works like a stack of plates in the kitchen sink — as each plate gets added, it’s placed on top of the stack. When the sink gets full, the plate that is added last is the first to be removed. Similarly, when a function is called, it’s added on top of the stack, and once it finishes executing, it’s removed from the top.

☕🚶‍♂️🚶‍♀️ Event Queue: The Event Queue holds asynchronous tasks waiting to be processed. It works like a line at a coffee shop. The first person in line is the first to get coffee, meaning the first task added in the queue will be the first one to be processed once the Call Stack is clear.

Step-by-Step Explanation of the JavaScript Event Loop Execution

For better visualization, all synchronous execution contexts are pushed onto the call stack, ready to be executed.

Image Alt Text

🚀 console.log('Start'); is executed immediately, producing "Start" as output.

⏸️ The Event Loop is paused while synchronous code runs in the main thread.

Image Alt Text

⏳ The setTimeout(() => { zeroSecondsLater(); }, 0); function is asynchronous.

🔄 The JavaScript engine delegates it to the Browser API.

Image Alt Text

📩 Since the delay is 0ms, the Browser API processes the request immediately and moves the zeroSecondsLater callback to the Event Queue.

Image Alt Text

🔄 setTimeout(() => { console.log('3 seconds later'); }, 3000); is delegated to the Browser API, which starts a 3-second timer.

Image Alt Text

⏳ Similarly, setTimeout(() => { console.log('4 seconds later'); }, 4000); is delegated to the Browser API, which starts a 4-second timer.

⏩ Meanwhile, synchronous code continues executing in the main thread without waiting for these timers.

🛑 Important: The Call Stack executes only synchronous functions and must complete all synchronous tasks before handling anything else. The Event Loop does not move callbacks from the Event Queue to the Call Stack until all synchronous code has finished executing.

Image Alt Text

🚀 console.log('End'); is executed immediately, producing "End" as output.

👀 At this point, the Call Stack is empty, signaling the Event Loop to check the event queue.

Image Alt Text

🎧 The Event Loop waits for the Call Stack to become empty and then moves the first callback from the Event Queue to the Call Stack for execution.

🔁 The zeroSecondsLater(); callback is moved from the event queue to the call stack.

Image Alt Text

📩 The Browser API completes the 3-second timer and moves console.log('3 seconds later'); to the Event Queue.

Image Alt Text

📩 One more second has passed so the Browser API moves console.log('4 seconds later'); to the Event Queue, placing it after the console.log('3 seconds later'); callback.

Image Alt Text

🔄 The zeroSecondsLater(); callback invokes oneSecondLater();.

Image Alt Text

🔄 oneSecondLater(); invokes console.log.

Image Alt Text

💬 "1 second later" is printed in the console.

Image Alt Text

🔄 Then, zeroSecondsLater(); invokes twoSecondsLater();.

Image Alt Text

🔄 twoSecondsLater(); invokes console.log.

Image Alt Text

💬 "2 seconds later" is printed in the console. All the synchronous code has been executed and the callback function zeroSecondsLater() completes its execution. At this point, the Call Stack becomes empty, signaling the Event Loop to begin processing callbacks from the Event Queue.

Image Alt Text

👀 The Event Loop moves console.log('3 seconds later'); from the Event Queue to the Call Stack.

Image Alt Text

🚀 The function executes, producing "3 seconds later" in the console.

Image Alt Text

👀 Again, the Call Stack is empty, allowing the Event Loop to move console.log('4 seconds later'); from the Event Queue to the Call Stack.

Image Alt Text

🚀 The function executes, producing "4 seconds later" in the console.

Image Alt Text

Key Takeaways

Only synchronous code is executed in the Call Stack (Execution Stack) on the main thread. The Event Loop does not move callbacks from the Event Queue to the Call Stack until all synchronous code has finished executing

✅ Asynchronous functions (e.g., setTimeout) are delegated to the Browser API, which processes them in parallel while synchronous code continues.

✅ Callbacks enter the Event Queue based on when they were processed by the Browser API, not necessarily in the order they were requested.

✅ The Event Loop waits for the Call Stack to be empty before moving a callback from the Event Queue to the Call Stack.


Thank you for reading!

I would be grateful to understand your opinion.