饾槓饾槯 饾槩 饾槺饾槼饾槹饾槰饾槼饾槩饾槷饾槷饾槮饾槼 饾槬饾槮饾槾饾槳饾槰饾槸饾槾 饾槩 饾槺饾槼饾槹饾槰饾槼饾槩饾槷, 饾槹饾槸饾槶饾樅 饾槱饾槩饾槶饾槯 饾樀饾槱饾槮 饾槴饾槹饾槪 饾槳饾槾 饾槬饾槹饾槸饾槮 饾槳饾槯 饾樀饾槱饾槮饾樅 饾槱饾槩饾樂饾槮 饾槹饾槸饾槶饾樅 饾槬饾槮饾槾饾槳饾槰饾槸饾槮饾槬 饾樀饾槱饾槮 饾槬饾槩饾樀饾槩 饾槾饾樀饾槼饾樁饾槫饾樀饾樁饾槼饾槮饾槾. -- 饾槢饾槱饾槮饾槸饾槰, 饾槕饾槹饾槸饾槮饾槾, & 饾槢饾槱饾槳饾槷饾槪饾槶饾槮饾槪饾樅
For this article I would like to shed light on a complex side in programming, and that is understanding the callstack.
I'll go over the JavaScript callstack as it's one of the most popular languages in todays programming ecosystem.
The callstack may seem a bit daunting (maybe like the godzilla gif below) "Everybody runnnn for your lives!!" But if you make it through this article, my hope is that it will provide you with better insight on the topic and give you a general idea of how the callstack operates.
I don't believe it's discussion is popularized in modern web development because it has many layers and is quite deep in theory. The word we usually use for that is "abstraction" - where you take something complicated and put a nice interface around it so that you don't have to deal with the complexities.
The callstack is a very low level concept, It goes deep in showing you all the layers of your program - including the framework code. That's why I believe understanding it will be of great use as you can pick apart where your code is going and what's happening to it.
I'll go over various topics such as:
How functions enter and exit the callstack and what happens if they never exit the callstack
How and why I believe the callstack is like a washing machine.
Why the callstack is an absolute necessity in coding
How the code we provide to our callstack (washing machine) can cause the callstack to not work as we expect
For our first example we'll start with an extremely basic recursive function:
(饾槯饾樁饾槸饾槫饾樀饾槳饾槹饾槸 饾槶饾槩饾樁饾槸饾槬饾槼饾樅() {
饾槶饾槩饾樁饾槸饾槬饾槼饾樅()
})();
As soon as you invoke this function it is sent to the callstack and creates what is known as a "stack frame" and will be temporaily stored until it has been returned.
If you visualize the callstack as a washing machine, we are bringing our laundry() function to the washing machine which is where it will be held until "washed" however, what would happen if we never took our laundry() out of the washing machine?
Great question! Notice anything odd about our function above? think about it for a minute. When you think you have it, scroll past Zach to review the answer
w潭e潭 潭n潭e潭v潭e潭r潭 潭s潭e潭t潭 潭a潭 潭s潭t潭o潭p潭p潭i潭n潭g潭 潭c潭o潭n潭d潭i潭t潭i潭o潭n潭!
That's right! This is what's known as stack overflow and a common problem we'll all eventually run into in programming, we never took our clothes out of the washing machine, and you may aswell throw a brick into it:
You'll also end up with a nasty error like the one below:
Our laundry() function kept running itself until our callstack reached its maximum request limit, or basically exploded like the laundring machine above!
The callstack does alot for us in the background of our app, its where our code is processed. But we need to also give it some guidance, for example:
If you ordered a pizza but gave no directions and no specific details for your pizza to the restaurant, would you expect the pizza to show up on time and with the right toppings?
Of course not, and the callstack has no idea what functions we want processed first and when to process them, it's there to run our functions.
This is why understanding the callstack "hierarchy" is important. As developers we want to have precedence on what order our functions are processed.
My goal for this next example is to visualize why ordering our functions is important if we want the right result and how our functions are entering the callstack, and how they will be able to "exit" the callstack to prevent our washing machine from blowing up.
Lets analyze the code below:
On line 12 we invoke decisionB (aka execute the script). All the functions in this script are then sent to our callstack to be processed. If we look into decision B we can see that it relies on decisionA, therefore decision A will need to be processed aswell to return the data we're are relying on for decisionB. How we can make sure decisionA also makes it to the callstack is including it before we execute our script on line 12 this is because JavaScript performs synchronously, meaning, one at a time and "top to bottom". Because we need to use the result of decisionA as data to for our decisionB we need to make sure we invoke decisionA so we can have it added to the callstack.
This is an example of a successfull execution in the callstack and returns our desired message:
"饾槈饾樁饾樅 饾槰饾槼饾槩饾槸饾槸饾樅 饾槾饾槷饾槳饾樀饾槱 饾槩饾槺饾槺饾槶饾槮饾槾, 饾槪饾槮饾槫饾槩饾樁饾槾饾槮 饾樀饾槱饾槮饾樅 饾槩饾槼饾槮 饾樀饾槱饾槮 饾槪饾槮饾槾饾樀";
So what will happen if we place our decisionA function after we send our script to the callstack from line 12? (example below)
We end up with this error because, our decision A is never added to our callstack and therefore cannot be processed by our callstack.
In our last example here we dived a bit deeper into theory. We should hopefully understand why our interface displaying the error "undefined" is not exactly the real reason for script not to run as we intended.
I hope after reading this, you've become even a bit more aquainted with what's happening behind the scenes when we invoke our functions.