🧠 "Call Stack executes any execution context that enters it. Time, tide, and JavaScript wait for none."
📌 TL;DR — Call Stack has no timer!
- What makes JavaScript asynchronous
- The role of Web APIs, Callback Queue, Event Loop, and Microtask Queue
- Visual understanding with event listener examples
- Priority between callbacks and promises
- Common interview questions on async JS internals
Although JavaScript is single-threaded and synchronous, browsers empower it using Web APIs, which include:
setTimeout()- DOM APIs
fetch()console.log(),localStorage, and others
These are NOT part of JavaScript but are accessible via the window object (the global environment in browsers).
window.setTimeout()
window.localStorage
window.console.log()console.log("start");
setTimeout(function cb() {
console.log("timer");
}, 5000);
console.log("end");
// Output: start -> end -> timer- Global Execution Context (GEC) created.
console.log("start")is executed.setTimeoutuses Web API to schedule thecb()callback after 5000ms.console.log("end")is executed.- Once 5s completes,
cb()is pushed to Callback Queue. - Event Loop pushes it to Call Stack when it's empty.
The Callback Queue stores functions to be executed later, while the Event Loop constantly checks if:
- Call Stack is empty ✅
- Callback Queue is not empty ✅ Then it moves items from Callback Queue → Call Stack.
console.log("Start");
document.getElementById("btn").addEventListener("click", function cb() {
console.log("Callback");
});
console.log("End");✅ Even after GEC is removed, the cb() in addEventListener lives in the Web API environment, waiting for the event to happen.
function printStr(str, cb) {
setTimeout(() => {
console.log(str);
cb();
}, Math.floor(Math.random() * 100) + 1);
}
function printAll() {
printStr("A", () => {
printStr("B", () => {
printStr("C", () => {});
});
});
}
printAll(); // Ensures A → B → Cconsole.log("Start");
setTimeout(function cbT() {
console.log("CB Timeout");
}, 5000);
fetch("https://api.netflix.com").then(function cbF() {
console.log("CB Netflix");
});
console.log("End");cbF()goes into the Microtask Queue after data is received.cbT()(from setTimeout) waits in Callback Queue.- Microtask Queue > Callback Queue (Higher priority)
- "Start" is logged immediately.
setTimeout(..., 0)registers the callbackcbTin the Callback Queue.Promise.resolve().then(cbP)registerscbPin the Microtask Queue.- "End" is logged next, since it's synchronous.
- Event Loop now steps in:
- It sees that the Call Stack is empty.
- It checks Microtask Queue first, finds
cbP, and executes it.- Logs:
"CB Promise"
- Logs:
- Then it checks Callback Queue, finds
cbT, and executes it.- Logs:
"CB Timeout"
- Logs:
Start
End
CB Promise
CB Timeout
This proves that:
- Even if
setTimeouthas0msdelay, it still goes to the Callback Queue. - Microtasks (like
.then()) have higher priority and get executed first.
| Feature | Microtask Queue | Callback Queue |
|---|---|---|
| Examples | Promise.then, MutationObserver |
setTimeout, setInterval, EventListeners |
| Priority | ✅ Higher | ❌ Lower |
| Risk of Starvation | ✅ Yes (if recursive) | ❌ No |
A: Immediately — it continuously runs in the background to monitor queues.
A: Only async ones (like setTimeout, fetch). Sync callbacks (map, filter) are not.
A: For holding ready-to-run async callbacks until the Call Stack is clear.
A: No. Callback waits for the Call Stack to clear — even 0ms may delay if stack is busy.
.then()callbacks from Promises- MutationObservers
- Tasks with higher execution priority than callbacks
- JS uses Call Stack to execute synchronous code.
- Web APIs offer asynchronous features like
setTimeout,fetch, and more. - Event Loop moves callbacks from queues to Call Stack when it's empty.
- Microtask Queue (Promises, MutationObserver) has higher priority than Callback Queue.
- Use
removeEventListenerand cleanup to avoid memory leaks due to closures. - JS is async-capable because of its runtime environment and browser support — not because of JS itself!






