Back to Blog Listing

How the JavaScript Event Loop Works Under the Hood

To write highly performant JavaScript, you must understand how its runtime executes code. Although JavaScript is a single-threaded language, it operates asynchronously through the Event Loop. Let's break down its architectural parts.

1. The Call Stack

The call stack is a LIFO (Last In, First Out) stack that tracks the currently executing function. When a function is called, it is pushed onto the stack; when it returns, it is popped off.

2. Web APIs / Node C++ APIs

Since the runtime thread can't block, async APIs (like setTimeout, fetch requests, or DOM events) are handed off to the browser's C++ Web APIs (or Node.js's libuv thread pool) to execute in the background.

3. Task Queue (Macrotasks) & Microtask Queue

When an async operation completes, its callback is moved into a queue waiting for execution:

  • Microtask Queue: Holds callbacks from Promises (.then, await reactions) and MutationObserver.
  • Callback Queue (Macrotasks): Holds callbacks from setTimeout, setInterval, and DOM events.
Critical Execution Rule: The Event Loop will prioritize the Microtask Queue over the Macrotask Queue. It will empty the *entire* microtask queue before moving on to execute a single macrotask callback.

Code Challenge: What is the Output?

console.log('Start');

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

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

console.log('End');

Answer:

  1. Start (Synchronous call stack)
  2. End (Synchronous call stack)
  3. Promise (Microtask runs before Macrotask)
  4. Timeout (Macrotask runs)
💻

Try this code in our online compiler!

Don't just read about it. Write, run, and experiment with these JavaScript concepts instantly on our online workspace with autocomplete suggestions.

Share this tutorial:

CodeCompile