[Advanced JS] What is the Event Loop? Discover How JavaScript Works

JavaScript, the language of the web, has a secret behind its ability to handle many tasks without getting "blocked": the Event Loop. For any JavaScript developer, from beginner to expert, understanding the Event Loop is not just optional—it's essential for writing efficient, optimized, and non-blocking code.

So what is the Event Loop? Imagine it as a smart traffic controller in a city with only one lane. This controller ensures that every "car" (task) moves in order, without causing traffic jams, even when some cars need to stop for a while to handle their own business.

What is the Event Loop? Discover How JavaScript Works

This article will help you deeply understand the Event Loop, from the most basic concepts to how it works in practice.

Why Do We Need the Event Loop? The Secret of the "Single Lane" 🛣️

To understand the Event Loop, we first need to grasp a core characteristic of JavaScript: single-threaded.

This means that at any given time, JavaScript can only execute one task. Like a person who can only do one thing at a time.

JavaScript Single Thread

So what's the problem?

Imagine you have a task that takes a lot of time, like downloading a large file from the network. If JavaScript executed this task sequentially, your entire website would "freeze". Users couldn't click buttons, scroll, or do anything until the file finished downloading. The user experience would be a disaster.

This is where the asynchronous model and the Event Loop shine. The JavaScript Engine (like Chrome's V8) works with the runtime environment (like the browser or Node.js) to create a smart processing mechanism, allowing long-running tasks to be handled in the background without blocking the main thread.

The Main Components of the JavaScript "Orchestra" 🎺

For the Event Loop to work, it needs the coordinated effort of several components. Think of them as an orchestra, each musician with a unique role.

1. Call Stack

This is the main "stage" where functions are executed. It works on a LIFO (Last-In, First-Out) principle.

Call Stack

  • When a function is called, it is pushed onto the top of the Stack.
  • When that function finishes and returns, it is popped off the Stack.
function greet() {
  console.log('Hello!')
}

function sayHello() {
  greet()
}

sayHello() // Start execution

In the example above, sayHello() is pushed onto the Stack, then greet() is pushed on top. greet() finishes, is popped off, then sayHello() finishes. The Call Stack is finally empty.

2. Web APIs

This is where asynchronous tasks are handled—heavy work we don't want to block the main thread. These APIs are not part of the JavaScript Engine, but are provided by the runtime environment (browser). Typical examples:

  • setTimeout, setInterval
  • DOM operations (e.g., addEventListener)
  • AJAX requests (fetch, XMLHttpRequest)

When the Call Stack encounters a call to a Web API (like setTimeout), it "delegates" this work to the browser and immediately moves on to the next task without waiting.

3. Callback Queue (Task Queue)

When an asynchronous task in the Web APIs finishes (e.g., setTimeout timer ends), its corresponding callback function (the function passed to setTimeout) is not executed immediately. Instead, it is pushed into a queue called the Callback Queue.

Callback Queue

This queue works on a FIFO (First-In, First-Out) principle.

4. Microtask Queue

This is a special queue with higher priority than the Callback Queue. It holds callbacks for more modern asynchronous tasks like Promise (then, catch, finally) and MutationObserver.

Key point: The Event Loop will always process all tasks in the Microtask Queue before touching any task in the Callback Queue.

Event Loop: The Tireless "Conductor" 🤵🏻

Now, let's connect all the pieces. The Event Loop has a single but crucial job:

Continuously check if the Call Stack is empty. If it is, it takes the first task from the queues (prioritizing the Microtask Queue) and pushes it onto the Call Stack for execution.

This loop runs endlessly, ensuring there is no downtime and tasks are handled efficiently.

Event Loop and How JavaScript Works

The complete process:

  1. JavaScript code starts running. Synchronous functions are pushed onto the Call Stack and executed immediately.
  2. When an asynchronous task (like setTimeout) is encountered, it is sent to the Web APIs for processing. JavaScript doesn't wait and continues executing the next commands in the Call Stack.
  3. When the Web API finishes the task (e.g., setTimeout timer ends), it pushes the corresponding callback into the Callback Queue (or Microtask Queue if it's a Promise).
  4. The Event Loop constantly monitors the Call Stack.
  5. As soon as the Call Stack is empty (all synchronous code has run), the Event Loop checks the Microtask Queue.
  6. If there are tasks in the Microtask Queue, the Event Loop takes the first one, pushes it onto the Call Stack, and executes it. It repeats this until the Microtask Queue is completely empty.
  7. After the Microtask Queue is empty, the Event Loop checks the Callback Queue. If there are tasks, it takes the first one and pushes it onto the Call Stack.
  8. This process repeats forever, forming an "event loop".

Classic Example

Look at the following code and guess the order of console output:

console.log('Start') // 1

setTimeout(() => {
  console.log('In setTimeout - Callback Queue') // 4
}, 0)

Promise.resolve().then(() => {
  console.log('In Promise - Microtask Queue') // 3
})

console.log('End') // 2

Step-by-step explanation:

  1. console.log("Start") is synchronous, pushed onto the Call Stack and executed immediately. Console outputs: Start.
  2. setTimeout is encountered. The Call Stack delegates it to the Web APIs. The timer is set to 0ms, but its callback still has to wait to be put into the Callback Queue.
  3. Promise.resolve().then(...) is encountered. The .then() is asynchronous. Its callback is immediately put into the Microtask Queue.
  4. console.log("End") is synchronous, pushed onto the Call Stack and executed. Console outputs: End.
  5. Now, the Call Stack is empty. The Event Loop starts working.
  6. The Event Loop checks the Microtask Queue first. It finds a callback from the Promise. This callback is pushed onto the Call Stack and executed. Console outputs: In Promise - Microtask Queue.
  7. The Microtask Queue is now empty. The Event Loop moves to the Callback Queue. It finds the callback from setTimeout.
  8. This callback is pushed onto the Call Stack and executed. Console outputs: In setTimeout - Callback Queue.

Final result:

Start
End
In Promise - Microtask Queue
In setTimeout - Callback Queue

Conclusion: Why Care About the Event Loop?

Understanding the Event Loop brings huge benefits:

  • Write non-blocking code: You can perform time-consuming tasks (API calls, file reading) without freezing the user interface.
  • Optimize performance: Arrange and prioritize tasks so your app runs more smoothly.
  • Debug effectively: Easily trace and understand why asynchronous code runs in an unexpected order.
  • Master advanced concepts: It's the foundation for understanding async/await, workers, and complex app architectures.

The Event Loop is a testament to the elegance and power of JavaScript's design. By mastering it, you're not just writing code—you're truly "conversing" with the language, controlling your app's flow professionally and efficiently.

Related Posts

[Advanced JS] What is a Closure? Advanced Applications of Closures in JavaScript

A closure is an advanced but extremely useful concept in JavaScript. This article will help you understand closures through intuitive examples, so you can apply them effectively in your projects.

[Advanced JS] What is an IIFE? Advanced Applications of IIFE in JavaScript

Learn about IIFE (Immediately Invoked Function Expression) and how it works in JavaScript. Discover practical uses of IIFE for creating scope and protecting variables.

[Advanced JS] Object-Oriented Programming (OOP) in JavaScript: Concepts & Examples

What is Object-Oriented Programming (OOP)? This article explains OOP concepts in JavaScript and provides practical examples to help you understand easily.

[Advanced JS] Data Structures & Algorithms: Practical Applications with JavaScript

Apply Data Structures and Algorithms to optimize web app performance. Level up your clean and efficient coding skills with JavaScript in this in-depth article.