WTF is the Stack Data Structure?

T Botell
8 min readJan 9, 2022

--

You may have come across the term “Call Stack” before in your programming adventures. What this term actually means may or may not have made sense to you at the time, but this article will clear that up for you as quickly as possible!

The Stack Data Structure:

Data structures might seem like a daunting concept, especially if you are new to the realm of programming and comp-sci. I can assure you that they are a fairly simple concept to grasp, especially when using some effective metaphors. So let’s get into it!

The stack data structure is what is known as a linear data structure, meaning that elements are stacked one on top of the other like a pile of books. For instance, for a long time I have unknowingly practiced the art of Tsundoku, which is a Japanese term for someone who owns a lot of unread literature. This means that I love books, but I never find myself actually getting around to reading any of them, so I just end up with many piles of books.

Artistic Rendering of my office — surrounded by stacks of books

One way I could go about getting through all these piles of books is by treating them like a stack data structure. To do this, I would start with the book that is at the top of the pile and work my way down, until there are no more books in the pile. This is very similar to how the call stack works.

For instance, I buy a book (a function gets called), that book gets put on the ground (gets placed on the call stack).

First book gets placed in the stack

I then buy another book which gets added to the pile (now two operations on the call stack).

Two books are now in the stack

I now read the top book and finish it (remove it from the call stack).

Most recently placed book gets read and removed from the top, leaving the book underneath

and now I can move on to the next book (remove it from the call stack). Now I have no more books in the pile (the call stack is clear)! Time to celebrate!

Celebration because I have now read all the books (the call stack is clear)

This is essentially how the stack data structure works — it can be seen as a pile of operations that get stacked, one on top of the other, and it can only execute from the top down (First in Last out or Last in First Out). In turn, operations can only be placed on top or removed from the top — they cannot be placed in the middle or on the bottom of the pile.

Stack data structures are incredibly useful — there is a reason why they are commonly used. Stack structures are most useful when the order of actions is very important.

Therefore, in a stack data structure, all operations take place at the “top” of the stack. As operations are called in your script, they are added onto the top. The operations on the stack are then executed from the newest (top) to the oldest (down). A great way to showcase this in action is through some simple Javascript using a function that relies on another function to execute.

Call Stack in Javascript

Javascript is what is known as a single-threaded language, meaning that it can only execute one thing at a time, and therefore utilizes only one call stack.

Here is a nice and easy example of this in action:

So first we define the add function — which will add two numbers together.

Then we define the average function, which uses our add function to add two numbers together, and then it divides the result of that operation in half to get the average.

We then call the function average with the arguments 20 and 40.

What happens in the call stack is:

  1. Javascript executes the average function call, which adds the average(20, 40) operation to the call stack
Average function gets added to the call stack

2. Inside our average function, there is a call to our add function. So that adds the add(20, 40) operation to the call stack, on top of the average(20, 40) operation.

Add function gets placed on top of average function in call stack

3. There are no more function calls in the script, so then Javascript executes the add(20, 40) operation, returning 60 and handing that back to average. Add() is removed from the top call stack because it is completed.

Add function is executed and removed from the top of the call stack

4. Now that add is finished, Javascript can finish the average(20, 40) operation. Which takes the 60 from our add() function and divides it in half, finally returning 30. Now average is completed and removed from the call stack.

Empty call stack

5. Now the call stack is officially empty, we have the result of our operation, and the call stack is done executing.

Now, this is a fairly simple example, but realistically, this is how it works even with more complex code. Depending on how operations are called, they get added to the call stack and then executed from top to bottom. Sometimes it can be more difficult to follow depending on the complexity of the code (and once you throw asynchronous operations into the mix) but it essentially stays the same no matter the complexity! So try your best to read through the code and follow through to see how it is going to execute, and remember console.log’s are your best friend — they are a great way of adding notes into the code to see the order of execution!

Async and the Call Stack

As I established in the last section, the Javascript engine executes a script from the top of the file and works its way down. Javascript then adds operations onto the call stack and executes them from the top down. The downside to this is that if there is a function that is being executed on the call stack (say at the top) that will take a long time to complete, it cannot move on to the next operation while that longer operation is executing. Therefore, this long operation becomes what is known as a “blocking function” — meaning it is blocking everything else from executing. This becomes detrimental when, for instance, you are working with a webpage. If that blocking function gets called, it will literally prevent any other operations from running. A good example of a blocking function can be a function that downloads a file from a remote server. Then, for instance, the long operation gets triggered by the user, while that operation is executing the user cannot click on anything else on the page. This is where Asynchronous functions come in.

To solve this problem, we have async functions. How async functions work is they get triggered, which then executes quickly, pops off to do what they are supposed to do, then once it has retrieved that data or finished executing, it pops back onto the call stack again, then once the call stack is clear it will give us the result of the long operation, preventing it from blocking. For instance:

Example of script with an async function

What will happen when this code executes is:

  1. ‘Start script…’ will output to the console
  2. Javascript will read setTimeout (which sets a task to be done after an allotted amount of time — in our code, it is set to 2 seconds) which will set ‘Download a file.’ To be console logged after two second
  3. ‘Done!’ Is output to the console
  4. 2 seconds later ‘Download a file.’ is logged to the console

What this means is that we can set functions to be run once the call stack (more specifically the event loop) is empty, and that prevents the issue of blocking functions. So what happens here is that the ‘Download a file.’ console log is executed two seconds after the event loop is finished.

Here is another example:

Another example of a script with an async function

Here the same thing as the last example will happen. But wait! We’ve set the timer to 0, doesn’t that mean that the console.log(‘Execute Right Away’) will happen immediately, before the console.log(‘Done!’)? Nope! The ‘timer’ being set to 0 means that it will execute immediately after the call stack is empty. So:

  1. The Javascript engine console.logs ‘Start Script…’
  2. It sets a timer to console.log(‘Execute Right Away’) 0 seconds after the call stack is cleared
  3. It console.logs ‘Done!’
  4. As soon as the call stack is cleared it console.logs ‘Execute Right Away’

This showcases the power of Asynchronous functions, as it prevents the blocking from ever taking place, enabling Javascript to essentially run multiple things at once. setTimeout is just one instance of an asynchronous function, and there are many more built into Javascript at your disposal.

One last thing. I mentioned something called the event loop earlier. If you are interested in learning more, and perhaps solidifying your understanding of exactly how Javascript works, I highly recommend this video:

Call Stack/Event Loop Talk

It is incredibly informative, and helped to solidify my understanding of the call stack and Javascript beyond just thinking of it as ‘magic’.

Conclusion

Hopefully, this article has helped to clear up exactly what the Call Stack is, how it is used, and some real-world applications of it in Javascript. It’s always a good idea to try to understand the Languages we are working in, especially on a deep level, so we can better understand how our programs are going to work and the order in which they will execute. Trust me, it’s not just random, although it might seem like it!

References

https://www.studytonight.com/data-structures/stack-data-structure

https://www.geeksforgeeks.org/stack-data-structure-introduction-program/

https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Introducing

https://www.javascripttutorial.net/javascript-event-loop/

https://www.w3schools.com/js/js_callback.asp

https://www.javascripttutorial.net/javascript-call-stack/

--

--