Asynchronous JavaScript & The EVENT LOOP โ€” From Scratch


Note: The Call Stack will execute any execution context that enters it โ€” no exceptions.
TL;DR: The Call Stack has no timer โ€” it just runs what's inside it, line by line.


Understanding JavaScript in the Browser

JavaScript, by design, is single-threaded and synchronous. But wait โ€” if it's synchronous, how does it handle things like network requests, timers, or user interactions without blocking the browser?

Hereโ€™s the secret: JavaScript alone canโ€™t do it all. It relies on the browser environment to handle tasks that would otherwise require multithreading or system-level capabilities.

What Powers the JavaScript Runtime?

When you run JavaScript in the browser, it works hand-in-hand with:

  • The JavaScript Engine (e.g., V8 in Chrome)

  • The Call Stack

  • The Web APIs (provided by the browser)

  • The Callback Queue

  • The Microtask Queue

  • And the Event Loop (the orchestrator)


๐Ÿ”Œ Web APIs: The Superpowers from the Browser

Web APIs are not part of JavaScript. Theyโ€™re features provided by the browser โ€” available to JavaScript via the global window object. Examples include:

  • setTimeout() โ€“ scheduling tasks

  • fetch() โ€“ making HTTP requests

  • document โ€“ interacting with the DOM

  • console.log() โ€“ printing to the dev console

  • localStorage โ€“ storing data in the browser

While we often use these without the window. prefix (like setTimeout()), behind the scenes they are accessed as window.setTimeout().


๐Ÿ› ๏ธ A Simple Asynchronous Code Example

Letโ€™s take a look at a basic async operation:

console.log("Start");

setTimeout(function cb() {
  console.log("Timer expired");
}, 5000);

console.log("End");

What Happens Here?

  1. A Global Execution Context (GEC) is created and pushed to the Call Stack.

  2. console.log("Start") is executed โ†’ โ€œStartโ€ is printed.

  3. setTimeout(cb, 5000) is encountered:

    • The setTimeout Web API is triggered.

    • The callback cb() is registered, and a 5-second timer starts.

  4. console.log("End") is executed โ†’ โ€œEndโ€ is printed.

  5. After 5 seconds, the timer expires โ€” but the callback cb() cannot go straight to the Call Stack.

  6. Instead, it goes to the Callback Queue.

  7. The Event Loop checks if the Call Stack is empty.

  8. Once the Call Stack is clear, it pushes cb() into it and executes โ†’ โ€œTimer expiredโ€ is printed.


๐Ÿ”„ Event Loop & Callback Queue Explained

The Event Loop is the invisible mechanism that ensures JavaScript remains non-blocking.

  • It constantly monitors the Call Stack and Callback Queue.

  • If the Call Stack is empty and thereโ€™s something in the Callback Queue, it pushes it onto the Call Stack.

  • This is how asynchronous code is eventually executed.

Why Do We Need a Callback Queue?

Imagine a button clicked six times rapidly:

document.getElementById("btn").addEventListener("click", function cb() {
  console.log("Button clicked");
});

Each click triggers the callback cb():

  • These callbacks are stored in the Callback Queue.

  • The Event Loop pushes them one by one into the Call Stack when it's free.

  • This queuing mechanism avoids chaos and maintains the execution order.


๐Ÿงฌ Promises & The Microtask Queue

Hereโ€™s where it gets interesting. Letโ€™s consider this:

console.log("Start");

setTimeout(function cbT() {
  console.log("Timeout callback");
}, 5000);

fetch("https://api.example.com").then(function cbF() {
  console.log("Fetch callback");
});

console.log("End");

Step-by-Step Breakdown:

  1. โ€œStartโ€ is logged.

  2. setTimeout() registers cbT() with a 5-second delay.

  3. fetch() initiates a network request and registers cbF() to be executed once the response arrives (say in 2 seconds).

  4. โ€œEndโ€ is logged.

  5. After 2 seconds, cbF() enters the Microtask Queue.

  6. After 5 seconds, cbT() enters the Callback Queue.

  7. The Event Loop gives priority to the Microtask Queue:

    • It empties the Microtask Queue first โ†’ cbF() is executed.

    • Then it processes the Callback Queue โ†’ cbT() is executed.

Output:

Start  
End  
Fetch callback  
Timeout callback

โš–๏ธ Microtask Queue vs Callback Queue

FeatureMicrotask QueueCallback (Task) Queue
PriorityHighLower
ExamplesPromise.then(), MutationObserversetTimeout(), setInterval(), click handlers
Execution TimingAfter current execution, before next taskAfter the current task completes
Risk of StarvationHigh (if tasks are recursive)Low

Starvation occurs if the Microtask Queue keeps adding new tasks recursively. This can delay or completely block the Callback Queue from being processed.


Common Interview Questions

1. What is the Event Loop in JavaScript?

The Event Loop continuously checks the Call Stack and Callback/Microtask Queues. If the Call Stack is empty, it pushes the next queued task (prioritizing Microtask Queue) onto the Call Stack.


2. Whatโ€™s the difference between Microtask Queue and Callback Queue?

Microtasks (like Promise.then) are processed before tasks in the Callback Queue (setTimeout, DOM events). Microtasks have higher priority.


3. Is console.log() part of JavaScript?

No. Itโ€™s part of the browserโ€™s console Web API, accessed via the window object.


4. What happens if setTimeout is set to 0ms?

Even with 0ms, the callback is queued and must wait for the Call Stack to be clear. So, it's never truly "immediate".


5. Why do event listeners stay in memory after code execution?

Event listeners are registered in the Web API environment. They stay alive to respond to events unless manually removed or the page is unloaded. This is why you should remove listeners when theyโ€™re no longer needed to avoid memory leaks.


6. Are synchronous callbacks also handled by the Web API environment?

No. Only asynchronous callbacks (like from setTimeout, fetch, etc.) are registered with Web APIs. Synchronous callbacks used with methods like map, filter, or reduce are handled directly by the JavaScript engine.


โœ… Final Thoughts

JavaScript may be single-threaded, but with the help of the browserโ€™s Web APIs, Callback and Microtask Queues, and the mighty Event Loop โ€” it becomes incredibly powerful and responsive.

Mastering this internal flow will transform your debugging skills, optimize your performance logic, and give you a major edge in interviews.

Stay curious. Debug deeply. Learn beyond the syntax. ๐Ÿš€


Best Video to learn this topic :

1
Subscribe to my newsletter

Read articles from UR Prakash Gupta directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

UR Prakash Gupta
UR Prakash Gupta

My name is ๐”๐‘ ๐๐ซ๐š๐ค๐š๐ฌ๐ก ๐†๐ฎ๐ฉ๐ญ๐š and I talk about ๐—ง๐—ฒ๐—ฐ๐—ต-๐Š๐ง๐จ๐ฐ๐ฅ๐ž๐๐ ๐ž, ๐—ช๐—ฒ๐—ฏ๐——๐—ฒ๐˜ƒ, ๐——๐—ฒ๐˜ƒ๐—ข๐—ฝ๐˜€ and ๐—Ÿ๐—ถ๐—ณ๐—ฒ๐˜€๐˜๐˜†๐—น๐—ฒ.