The call stack is how JavaScript keeps track of which function is running and where to go back to when it finishes. Calling a function puts a new frame on top of the stack; returning takes it off. Only the frame on top runs, so JavaScript does one thing at a time.
Try it
In this example one function calls another. Press Next step and watch frames being added and removed in the Call stack panel, top first.
Code (current line highlighted)
Call stack (top first)The function running right now is on top. Calling a function adds a frame; returning removes it. Only one frame runs at a time.
Web APIs (timers waiting in the browser)The browser counts timers outside JavaScript. When a timer finishes, its callback moves to the task queue.
Microtask queue (next first)Promise callbacks (.then, code after await, queueMicrotask) wait here. Once the call stack is empty, all of them run before the next task.
Task queue (next first)Timer callbacks wait here. The event loop takes one task, runs it, then empties the microtask queue before taking the next.
Console output
Scope and variables (yellow = changed this step)
Timeline (click a block or use the arrow keys)Script, function calls and returnsawait and microtasksTimers and tasksConsole outputErrors
Step by step: what the example does
function greet(name) {
return "Hello, " + name;
}
function welcome() {
const msg = greet("Ada");
console.log(msg);
}
welcome();
console.log("done");- The script starts as the main script frame, at the bottom of the stack. The two function declarations are registered, but nothing inside them runs yet.
welcome()is called on line 10, so awelcome()frame goes on top of the main script.- Inside
welcome, line 6 callsgreet("Ada"). Agreet()frame goes on top. It has its own variablename, andwelcomepauses, waiting for the result. greetreturns"Hello, Ada". Its frame is removed, andwelcomecontinues exactly where it stopped, storing the value inmsg.welcomeprints Hello, Ada, finishes, and its frame is removed too.- Back in the main script, line 11 prints done. Then the main script finishes and the stack is empty.
What a stack frame holds
Each frame belongs to one function call and remembers:
- which function is running and the line it has reached;
- its parameters and local variables (its own execution context);
- where to continue, and where to send the return value, when it finishes.
Because a new frame is created for every call, two calls of the same function never share local variables, even if they run at the same time in the stack, as in recursion.
Last in, first out
A stack works like a pile of plates: the last frame added is the first one removed. A function can only finish after every function it called has finished. That is why greet above returns before welcome, even though welcome started first.
Recursion: a function calling itself
function factorial(n) {
if (n <= 1) return 1; // base case: stop here
return n * factorial(n - 1); // waits for the smaller call
}
console.log(factorial(4)); // 24Each call waits for the next, smaller one, so the stack grows to four factorial frames (n = 4, 3, 2, 1). Then the frames come off one by one, each multiplying the value it got back: 1, 2, 6, 24. Paste this code into the visualizer with Edit code to watch the stack grow and shrink.
Stack overflow: "Maximum call stack size exceeded"
The stack has a size limit. A recursive function without a working base case keeps adding frames until the engine stops it with an error: RangeError: Maximum call stack size exceeded in Chrome and Node.js, or InternalError: too much recursion in Firefox. The fix is almost always a missing or unreachable base case, such as a condition that never becomes true.
One stack, one thing at a time
JavaScript has a single call stack per thread. While a long function is running, nothing else can: not a timer, not a click handler, not a promise callback. Callbacks wait outside the stack until it is empty, and the event loop decides which one runs next.
Common mistakes
- Thinking a
setTimeoutcallback is on the stack while it waits. It isn't. It only gets a frame when the event loop runs it, after the current code has finished. - Recursion without a base case, or with a base case that is never reached (for example counting up when the condition checks for a smaller number).
- Reading a stack trace upside down. In an error's stack trace, the first line is the function that was on top of the stack when the error happened; the lines below are the functions that called it.
What LoopLens simulates
The order of calls and returns is real: LoopLens runs your code in its own interpreter and records each step. The stack you see is a picture of that interpreter's state, with the top-level code shown as main script. LoopLens stops a run after 3,000 steps or 40,000 operations to keep the page responsive, so very deep recursion ends with LoopLens's own limit message instead of a real "Maximum call stack size exceeded" error.