Async JavaScript: The Call Stack

Visualizing the Call Stack

Jake Mills
5 min readApr 14, 2021
Photo by Icons8 Team on Unsplash

Contrary to what the image above depicts, the call stack is a relatively easy concept to understand; visualizing it may be a different story. As always, we turn to MDN to give us the tea:

A call stack is a mechanism for an interpreter (like the JavaScript interpreter in a web browser) to keep track of its place in a script that calls multiple functions — what function is currently being run and what functions are called from within that function, etc.

MDN

Voila! A stack being built

I love MDN. Sometimes, it takes me a few read-throughs when the documentation is full of technical jargon, but this time it’s straight and to the point. The JS interpreter uses the call stack mechanism to keep track of its place. This idea is based upon the abstract data structure known as a stack that follows a particular order, last in first out (LIFO). This term ‘stack’ can be visualized by a stack of, well, anything. Think of it as a standard draw pile from a card game, a stack of dishes in the sink you’ve neglected for days (👀 #roommates), or, as the gif shows, a stack of books.

The stack of books gif does not depict the order in which elements come off the stack. LIFO, last in first out, means the last element added to the stack will be the first element that is removed from the stack. Looking at the gif to the left of various candy, you can see this idea at work. The stack will continue to increase in its sugary height, but to remove the candy at the base of the stack, we have to remove all other candies on top in the order they were ‘stacked’ first. Hence, LIFO.

Now that we have a basic idea of how stacks work in computer science, let’s talk about the JavaScript interpreter’s call stack. When a script calls a function, the JS interpreter adds that function to the call stack and begins to run the function. If this first function calls any functions from within, they are added to the top of the call stack and run when their calls are reached. The stack will continue to grow with every function that is called within our functions. When the current call stack of functions finishes, the interpreter will remove the stack's initial function. Finally, the interpreter will continue the execution of code from where it left off once the call stack is empty.

I know that paragraph can be hard to follow, so here is MDN saying the same thing a bit differently:

— When a script calls a function, the interpreter adds it to the call stack and then starts carrying out the function.
— Any functions that are called by that function are added to the call stack further up, and run where their calls are reached.
— When the current function is finished, the interpreter takes it off the stack and resumes execution where it left off in the last code listing.
— If the stack takes up more space than it had assigned to it, it results in a “stack overflow” error.

MDN

Let’s try a brief visual demonstration to depict the call stack at work.

The code above is a somewhat inefficient way to find the circumference of a circle. I say it’s inefficient because this could all be combined into one function, but this method will show a simple use of the call stack. To visualize this call stack, we will use the Chrome DevTools.

  1. First, navigate to the sources tab (1) & find the JS file you want to inspect (2).

2. Click next to the spot you want to add a breakpoint to in your code (3). This will allow the debugger tool to initialize on your code. You’ve done this successfully if you see a breakpoint appear under the proper tab (3a).

3. Run the code (refresh the page), and you will see the debugger tool at work. It will stop at the breakpoint. You’ll notice in our code that the call stack has been initialized as an anonymous function. This signifies to the interpreter to find the reference of this particular function within the code.

4. Once the interpreter finds the reference to the function; it will add that function to the call stack. You’ll notice under the Scope tab that you can see the values to various elements used within the function, i.e. radius, pi, etc.

5. Since our function has another function within it, the interpreter now has to find the reference to that function. Once found, it adds the function (twoPi) to the call stack.

6. It now finds the return value of the function since no other functions are called within twoPi.

7. With the return value of twoPi in hand, the interpreter removes twoPi from the top of the stack and heads back to the initial function of circumference. Since no other functions are within circumference, it can find and return the value of circumference.

8. Now that the return value has been given, the interpreter removes the call stack's initial function and moves through the rest of the code.

That’s it! You just kind of witnessed the inner workings of JavaScript’s call stack. Now, I’m sure there are better ways to show this at work. I actually made a gif of all of these steps, but it came out blurry on medium, and I gave up (haha 😅). Short of making a video, this was where I landed. I hope it was helpful! And tune in for more JS blogs soon. I think I’m going to explore even more the async nature of JavaScript in the future.

Happy Coding 🤓

--

--

Jake Mills

Software Engineer hailing from the Empire State, writing about what interests me and hoping someone else finds it interesting too. 👨🏻‍💻 🤓 He/Him #LFGM