Credit: auksolution661454 /

The JavaScript Call Stack

What happens when functions call other functions in JavaScript? How does JavaScript keep track of these function calls to know where it is?

The call stack.

The Stack

A Stack

A stack is a data structure that stores a linear collection of elements. Interacting with the stack occurs via two operations: push and pop.

  1. Push adds a new element to the top of the stack
  2. Pop removes the current element at the top of the stack

Limiting access to stack elements to the ‘push’ and ‘pop’ operations makes the stack a Last-In-First-Out (LIFO) data structure. Unlike an array, you do not modify or access any element in the stack except the top one. This ensures the order of elements in the stack remains stable, and you can only access them in reverse of the order they were added.

A stack will have a predefined size limit, after which trying to push an additional element on to the stack will result in a stack overflow error (hmm... sounds familiar).

A stack some of us might might recognize. Credit: supernatone /

Some common examples of stacks in practice include your browsers back/forward buttons, and the undo/redo functions in your favorite text editor.

How could I implement a stack?

So we know what a stack is… how does JavaScript use it to manage function calls?

The Call Stack

const foo = () => console.log("foo");const bar = () => foo();const baz = () => bar();

At this point, the only element in the stack is the ‘main’ function which represents the file itself.

Now let’s call baz:


Immediately, baz is pushed to the stack. It also calls a function, bar , which calls foo , which calls console.log. This is what our call stack looks like when those are invoked:

At this point the console.log is executed:

> foo

console.log , foo, bar, and baz are now finished executing, and are popped off the stack:

The call stack is empty, there are no more statements to run, and the program exits.

We can view the call stack by throwing an error in foo:

const foo = () => {  throw Error("foo");};const bar = () => foo();const baz = () => bar();baz();


throw Error("foo");
Error: foo
at foo (/test.js:2:9)
at bar
at baz
at Object.<anonymous> (/test.js:9:1)

We can see foo was called by bar which was called by baz. This is what our call stack looks like when the error is throw:

How big can the call stack be?

"Maximum stack size is 13947 in your current browser"

We can also see the error that is thrown when the limit is exceeded:

"RangeError: Maximum call stack size exceeded"

The Event Loop, Message Queue, and Async

Read more in this explanation from Flavio Copes.


  • The call stack in JavaScript keeps track of function calls, pushing to the stack when the function is invoked and being popped off the stack once it’s finished
  • The stack size limit is determined by many different factors, and exceeding this limit will result in a RangeError
  • The call stack executes code synchronously — async functions will wait in the message queue until the call stack is empty, at which point the event loop pushes them onto the call stack to run