The Call Stack
The call stack is where JavaScript keeps track of currently running function calls.
JavaScript runs one stack frame at a time.
When a function is called, it goes onto the stack.
When the function finishes, it comes off the stack.
Simple Example
function greet() {
console.log("Hello");
}
greet();When greet() is called:
greetis pushed onto the call stack.console.logruns.greetfinishes.greetis popped off the stack.
Nested Function Calls
function first() {
second();
}
function second() {
third();
}
function third() {
console.log("Done");
}
first();Call order:
first()
second()
third()
console.log()Stack grows as functions call other functions.
Then it unwinds as functions finish.
Stack Diagram
At the deepest point:
console.log
third
second
first
globalThe top of the stack runs first.
first cannot finish until second finishes.
second cannot finish until third finishes.
Synchronous Code Uses the Stack
console.log("A");
console.log("B");
console.log("C");This runs in order because each operation finishes before the next begins.
Output:
A
B
CBlocking the Stack
If one function takes a long time, the stack is blocked.
console.log("Start");
for (let i = 0; i < 1_000_000_000; i++) {}
console.log("End");The second log cannot run until the loop finishes.
In the browser, this can freeze the UI.
Stack Overflow
If functions keep calling without stopping, the stack can overflow.
function recurse() {
recurse();
}
recurse();This eventually throws an error like:
RangeError: Maximum call stack size exceededThis usually happens with recursion that has no base case.
Correct recursive functions need a stopping condition.
function countDown(number) {
if (number <= 0) {
return;
}
countDown(number - 1);
}Async Code and the Stack
Async callbacks do not run while the stack is busy.
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
console.log("C");Output:
A
C
BThe timer callback waits until the current stack is clear and the event loop picks it later.
Stack Traces
When an error occurs, JavaScript often shows a stack trace.
function first() {
second();
}
function second() {
throw new Error("Failed");
}
first();The stack trace helps you see the path of function calls that led to the error.
This is useful for debugging.
Best Practices
Keep long-running synchronous work small.
Avoid blocking the main thread in browser code.
Use stack traces to debug where errors came from.
Always include a base case in recursion.
Remember that async callbacks wait until the call stack is clear.
Common Mistakes
Mistake 1: Thinking setTimeout(..., 0) Runs Immediately
It does not interrupt the current stack.
It schedules work for later.
Mistake 2: Writing Recursion Without a Base Case
function loop() {
loop();
}This eventually overflows the stack.
Mistake 3: Blocking the UI With Heavy Loops
Long synchronous loops can prevent clicks, rendering, and timers from being handled promptly.
Summary
The call stack tracks currently running JavaScript function calls.
- Function calls are pushed onto the stack.
- Finished functions are popped off.
- JavaScript runs the top of the stack.
- Long synchronous work blocks later work.
- Async callbacks wait until the stack is clear.
- Stack overflow can happen with runaway recursion.
- Stack traces show the chain of function calls that led to an error.