Let’s start with a simple example. You have some code that is dependent on some variable to exist in memory. As long as it is not there, you’d like the code to retry again.
The result of this code is the following error:
One can simply see this error, find out that the solution would be to use some asynchronous mechanism and come out happy. If you just want the solution, skip the next part.
If you’d like to learn how this error happens, what is the call stack and what are synchronous and asynchronous events in the browser – read on.
The famous website – stackoverflow – has a funny logo:
Actually – the term
stack overflow is exactly what happened to our little code. Our function called itself multiple time and filled the call stack – hence we exceeded the maximum call stack size. We overflowed it with callback calls.
What is this call stack?
The call stack is, well… a stack. A stack is a data structure. It’s a kind of array that allows us two basic operations: add to the stack and pop the last added value.
When we call a function it is added to the call stack. When this function calls a function, it is also added to the call stack (let’s call that called function a child).
When a function finishes running, it is removed from the stack. Remember that in a stack, we remove only the last added value. If the function has children, it needs to wait until all of them finish.
Here’s an illustration:
So what’s stackoverflow?
Let’s take what we learned about the call stack. If our function calls itself over and over again, the call stack will never clear. It will just add more and more calls to the same function:
So stack overflow would be the case of a function that calls itself indefinitely:
How does it look in the browser?
Going back to our retry function, and knowing what we know now about the callstack, we can now understand what’s the
Maximum call stack size exceeded error mean.
The call stack can be seen in the browser and not just in fancy gif illustrtaions.
I run the above code in an HTML page and record using the performance tab. Here are the results:
In Figure 5 the retry function is called 688 times (the pink lines). After 688 times, we ran out of call stack space and the app crashed.
Not only our code doesn’t do what we want – it crashes. In order to be able to do what we want – retry periodically until something external happens – we’ll need to make our function asynchronous.
Making our function asynchronous is quite easy. Using
setTimeout can do just that:
When running this code, after around 1 second, we get
That is the question! logged in the console.
Let’s look at the recording result of this code:
The results in Figure 6 show that it took around 1 second to run the final call – which eventually logged the wanted result. In between, we had asynchronous calls to the function in roughly 250ms delay between each call.
We now have our retry mechanism. Problem solved!
Which came first, the chicken or the egg?
What about asynchronous processes, like the one created by
setTimeout? Or promises and ajax calls?
Here we go out from the zone of the Call Stack to an area called the Event Loop.
When we set our timeout, what we actually did was ask the system to take the callback given and add it to a callback queue once the timer runs out.
click function finishes, it is removed and the event loop now has an empty stack to send
logger to once it is set in the callback queue.
If we had more asynchronous events set in between they would be in the callback queue as well – and our
logger might just had to wait a bit longer until they finish and the call stack would be ready for it.
This is, in a nutshell, how asynchronous process come to be.
Not all asynchronous processes were born equal
There are types of asynchronous calls. For instance – there’s the setTimeout call, promises. I/O (e.g. user interaction) etc. Each has a different priority to enter the Event Loop.
For instance, if we have a callback from a
setTimeout and a callback from a resolve
Promise – the
Promise‘s callback wins and gets to the call stack first.
I won’t go into more details than that in this article.
Let’s recap our journey so far:
- We wanted to create a retry mechanism to some variable on the global scope (window).
- We saw that naively trying to call a retry function will results in stackoverflow. Hence, our solution was to make the consecutive calls to
- We did that using
setTimeoutand it solved our issue.
I hope the mechanism is a bit clearer now. As usual, leave comments below or message me directly if you’d like to debate over this (or anything else 🙂 ).