usha
Login
Back to Blog
June 12, 20264 min read67 views

The Call Stack — A Tower of Plates

You have seen error messages in JavaScript. That wall of text with function names and line numbers? That is the call stack talking to you. Once you understand what the call stack is, those error messages stop being scary and start being genuinely useful.

What is the call stack?

The call stack is how JavaScript keeps track of where it is in your code at any given moment. Every time a function is called, JavaScript needs to remember two things: what is currently running, and where to go back when it is done. The call stack handles both.

Think of it like a stack of plates in a cafeteria. You can only add a plate to the top, and you can only take one from the top. The last plate placed is the first one removed. This is called LIFO — Last In, First Out. The call stack works exactly the same way, but with function calls instead of plates.

How it works in practice

Every time JavaScript calls a function, it places a new "frame" on top of the stack. That frame holds the function's local variables and tracks where to return when the function finishes. When the function returns, its frame is removed, and JavaScript picks up right where it left off in the previous function.

Here is a simple example:

javascriptfunction c() { console.log("C is running"); }

function b() { c(); console.log("B is running"); }

function a() { b(); console.log("A is running"); }

a();

Step by step, the stack looks like this:

a() is called → stack: [a]

a calls b() → stack: [a, b]

b calls c() → stack: [a, b, c]

c() finishes → stack: [a, b] — prints "C is running"

b() finishes → stack: [a] — prints "B is running"

a() finishes → stack: [] — prints "A is running"

Each function waits patiently while the ones above it finish. When they are done, they get removed, and the one below continues.

Reading a stack trace

When JavaScript hits an error, it prints a stack trace — a snapshot of the entire call stack at the moment things went wrong. Most beginners glance at it and panic. Once you know how to read it, it tells you exactly what happened.

javascriptfunction processUser(user) {

return user.name.toUpperCase(); // crashes if user is null

}

function loadUser() {

const user = null;

return processUser(user);

}

loadUser();

// Cannot read properties of null (reading 'name')

// at processUser (script.js:2)

// at loadUser (script.js:7)

// at script.js:10

Read it top to bottom. The top line is where the error actually occurred — inside processUser. The lines below show how the code got there: loadUser called processUser, and loadUser was called from the top of the script. Follow the chain upward and you can pinpoint exactly where things went wrong and why.

Stack overflow — now it makes sense

If you read the recursion chapter, you have already seen a stack overflow. Now you know exactly why it happens. When a recursive function has no base case, it keeps calling itself — and JavaScript keeps adding frames to the stack. Eventually the stack hits its limit and crashes with RangeError: Maximum call stack size exceeded.

javascriptfunction infinite() {

return infinite(); // adds a new frame every time, forever

}

infinite(); // RangeError: Maximum call stack size exceeded

The stack ran out of room. That is all a stack overflow is — too many frames, no space left.

(And yes — the website Stack Overflow is named after this exact error.)

How the call stack connects to the event loop

The call stack and the event loop work as a team. The event loop has one simple rule: only push a new task onto the call stack when the stack is completely empty.

This is why asynchronous callbacks — things like setTimeout or data fetched from a server — never interrupt your running code. JavaScript waits for the current call stack to clear, then picks up the next waiting task. Your running code is never cut off mid-execution.

Chat on WhatsApp