What is the Call Stack?
The call stack is a mechanism that keeps track of which functions are currently running and which one called which.
You can think of call stack like a stack of plates. JavaScript uses it to keep track of which function is currently running.
- When a function runs, it’s added to the top of the stack.
- When it finishes, it’s removed from the top.
- This way, JavaScript always knows what to do next.
It follows the Last In, First Out (LIFO) principle. The last function added is the first to be removed.
For example:
function sayHi() {
console.log("Hi!");
}
function greet() {
sayHi();
}
greet();
Behind the Scenes:
greet()
is called, added to the call stack.greet()
callssayHi()
,sayHi()
is added on top.sayHi()
runs and logs"Hi!"
, then it’s removed from the stack.greet()
finishes and also removed from the stack.- The stack is empty again.
This is how JavaScript keeps track of which function to run, and in what order.
What is the Event Loop?
JavaScript is single-threaded, that means it can run only one thing at a time.
But still, it handles:
- Delays (
setTimeout
) - API calls (
fetch
) - User actions (like clicks or typing)
So how does JavaScript appear to do many things at once?
That's where the Event Loop comes in.
For example:
console.log("Start!");
setTimeout(() => {
console.log("Timeout!");
}, 0);
console.log("End!");
// Output:
// Start!
// End!
// Timeout!
Here,
console.log("Start!")
runs first, it goes on the call stack and executes.setTimeout(..., 0)
tells JavaScript: "Run this later, after 0ms, just put it in the task queue."console.log("End!")
runs next.- Now the call stack is empty.
- The event loop checks the task queue.
- It finds the
setTimeout
callback and moves it to the call stack. - Now the timeout callback runs, "Timeout!".
This whole process is the event loop doing its job, making JavaScript feel asynchronous even though it runs one thing at a time.
How It All Works Together
- Call Stack: Handles synchronous code (function calls, math, etc.)
- Web APIs (in browsers): Handle things like
setTimeout
, DOM events,fetch
. - Callback/Task Queue: Stores callback functions that should run after the stack is clear.
- Event Loop: It keeps checking: "Is the stack empty? Okay, let me push the next task from the queue."
Example
console.log("1");
setTimeout(() => {
console.log("2");
}, 0);
Promise.resolve().then(() => {
console.log("3");
});
console.log("4");
// Output:
// 1
// 4
// 3
// 2
Here,
1
and4
are synchronous, run immediately.Promise.then
goes to the microtask queue.setTimeout
goes to the callback queue (macrotask).- Microtasks (like Promises) run before macrotasks.
- So
3
runs before2
.
Quick Note on Microtasks and Macrotasks
Microtasks are small tasks that run immediately after the current script finishes and before any rendering or other events. This includes Promise.then
callbacks and queueMicrotask
.
Macrotasks, like setTimeout
and setInterval
, run later, after all microtasks complete and the browser has had a chance to update the UI.
This is why even a setTimeout
with 0ms
delay runs after Promise callbacks.
Microtasks vs Macrotasks
Queue Type | Examples | Priority |
---|---|---|
Microtask | Promise.then , queueMicrotask |
High |
Macrotask | setTimeout , setInterval |
Lower |
The event loop always finishes all microtasks before moving to the next macrotask.
Infinite Loop Danger
setInterval(() => {
while (true) {}
}, 1000);
This will block the event loop.
- Even though
setInterval
runs every second, - The infinite
while (true)
keeps the stack busy. - So no other callbacks or UI updates happen.
- This freezes the page because the event loop can’t process anything else.
Why It Matters
Understanding the call stack and event loop helps you:
- Debug async code better
- Avoid callback hell
- Use Promises correctly
- Know when things really happen
Best Practices
- Keep your synchronous functions short to avoid blocking the stack.
- Use
Promise
andasync/await
for readable async flow. - Avoid blocking loops or long computations in the main thread.
- Be aware of microtask vs macrotask behavior when debugging timing issues.