DEV Community

Cover image for JavaScript Event Loop Explained: A Beginner's Guide With Examples
BuildWithGagan
BuildWithGagan

Posted on ā€¢ Edited on

17 1 1 1 2

JavaScript Event Loop Explained: A Beginner's Guide With Examples

Introduction

JavaScript is a single-threaded programming language, meaning it executes one operation at a time. However, it is also capable of handling asynchronous tasks efficiently. The event loop is the secret behind JavaScript's ability to handle multiple tasks asynchronously without blocking the main thread.

Understanding the event loop is crucial for mastering JavaScript, as it directly affects performance, responsiveness, and execution order. In this article, we will break down the event loop step by step with detailed examples and real-world use cases.


What Is the Event Loop?

The event loop is a mechanism in JavaScript that continuously monitors the call stack and the task queue, ensuring that asynchronous operations like API calls, timers, and event listeners are executed correctly without blocking the main thread.

How Does the Event Loop Work?

  1. JavaScript Executes Code Synchronously: The engine executes code line by line.
  2. Asynchronous Tasks Are Handled Separately: Tasks like setTimeout, API calls, and event listeners are delegated to the Web APIs.
  3. Tasks Move to the Callback Queue: Once completed, they are pushed to the callback queue (or microtask queue for promises).
  4. Event Loop Checks the Call Stack: If the stack is empty, it picks the next task from the queue and executes it.

Understanding the Call Stack

The call stack is a data structure where JavaScript keeps track of function execution. Whenever a function is invoked, it gets pushed onto the stack, and when execution completes, it is popped off the stack.

Example:

function first() {
    console.log("First function");
}

function second() {
    console.log("Second function");
}

first();
second();
Enter fullscreen mode Exit fullscreen mode

Execution Flow:

  1. first() is called and pushed onto the stack.
  2. console.log("First function") executes and prints.
  3. first() is popped from the stack.
  4. second() is called, pushed onto the stack, executed, and removed.

How JavaScript Handles Asynchronous Code

JavaScript uses Web APIs (in browsers) or Node.js APIs to handle asynchronous operations like:

  • setTimeout and setInterval
  • DOM events (click, keydown, etc.)
  • HTTP requests (e.g., fetch, XMLHttpRequest)
  • Promises and async/await

When these operations are triggered, they are handled outside the call stack and moved to a queue when completed.


The Task Queue and Microtask Queue

Task Queue (Macro-task Queue)

  • Contains callbacks from setTimeout, setInterval, and I/O tasks.
  • Executes only when the call stack is empty.

Microtask Queue

  • Contains callbacks from Promises and MutationObserver.
  • Executed before the task queue whenever the call stack is empty.

Example of the Event Loop in Action

console.log("Start");

setTimeout(() => {
    console.log("Timeout Callback");
}, 0);

Promise.resolve().then(() => {
    console.log("Promise Callback");
});

console.log("End");
Enter fullscreen mode Exit fullscreen mode

Expected Output:

Start
End
Promise Callback
Timeout Callback
Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. console.log("Start") runs first (synchronous, added to the stack).
  2. setTimeout is called, delegated to the Web API, and scheduled for later.
  3. Promise.resolve().then(...) is executed, and its callback is placed in the microtask queue.
  4. console.log("End") runs.
  5. The event loop first processes the microtask queue, executing the promise callback.
  6. Finally, the setTimeout callback runs.

Promises vs. setTimeout: Which Executes First?

Since microtasks (Promises) have higher priority than macrotasks (setTimeout), promises execute first.

Example:

setTimeout(() => console.log("Timeout"), 0);
Promise.resolve().then(() => console.log("Promise"));
Enter fullscreen mode Exit fullscreen mode

Output:

Promise
Timeout
Enter fullscreen mode Exit fullscreen mode

Real-World Use Case: Handling API Requests

In real applications, understanding the event loop is crucial for handling asynchronous operations like API calls efficiently.

console.log("Fetching data...");

fetch("https://jsonplaceholder.typicode.com/todos/1")
    .then(response => response.json())
    .then(data => console.log("Data received:", data));

console.log("Other operations...");
Enter fullscreen mode Exit fullscreen mode

Execution Flow:

  1. console.log("Fetching data...") runs.
  2. fetch starts the request and is delegated to the Web API.
  3. console.log("Other operations...") runs.
  4. Once the response arrives, the callback is placed in the microtask queue and executed after synchronous tasks.

Common Mistakes and How to Avoid Them

1. Assuming setTimeout(fn, 0) Runs Immediately

It does not run immediately; it waits for the call stack to clear first.

2. Ignoring Microtask Queue Priority

Promises execute before timers, so don't assume setTimeout will execute first.

3. Blocking the Event Loop

Avoid long-running synchronous operations as they block the event loop.

setTimeout(() => console.log("Done"), 1000);

while (true) {} // Blocks execution, preventing timeout from running
Enter fullscreen mode Exit fullscreen mode

Fix: Move heavy computations to Web Workers or use setTimeout to break tasks into chunks.


FAQs

1. What Is the Purpose of the Event Loop?

It ensures that asynchronous code executes efficiently without blocking synchronous execution.

2. Does JavaScript Run on Multiple Threads?

No, JavaScript is single-threaded, but asynchronous tasks run in the background using Web APIs.

3. What Is the Difference Between Macro and Microtasks?

Microtasks (e.g., Promises) have higher priority and execute before macrotasks (e.g., setTimeout).

4. How Can I Optimize JavaScript Performance?

  • Use asynchronous operations wisely.
  • Avoid blocking the main thread.
  • Use Web Workers for heavy computations.

5. Why Is setTimeout(fn, 0) Delayed?

It does not run immediately because it must wait for the call stack to be empty.


Conclusion

The event loop is at the heart of JavaScript's asynchronous behavior. By understanding the call stack, task queue, microtask queue, and Web APIs, you can write more efficient, non-blocking code.

Mastering the event loop is essential for debugging performance issues, handling API requests efficiently, and improving user experience. Keep practicing, experiment with different scenarios, and soon, you'll be a JavaScript event loop expert!

Image of Quadratic

Free AI chart generator

Upload data, describe your vision, and get Python-powered, AI-generated charts instantly.

Try Quadratic free

Top comments (1)

Collapse
 
programminginblood profile image
Programming In Blood ā€¢
Comment hidden by post author

Some comments have been hidden by the post's author - find out more

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

šŸ‘‹ Kindness is contagious

DEV is better (more customized, reading settings like dark mode etc) when you're signed in!

Okay