Why setTimeout Does Not Guarantee Time to Execution
I want to go deeper into the asynchronicity of Javascript through a setTimeout example and why it is not a guarantee of time to execution.
First Lesson
You probably first learned about the asynchronicity of Javascript with setTimeout. The example could go something like the following:
const logOne = () => console.log(1);
const logTwo = () => console.log(2);
const logThree = () => console.log(3);
logOne();
setTimeout(logTwo, 1000);
logThree();
You learned that the order of the logs is:
1
3
2
I want to go deeper into the asynchronicity of Javascript through this example and the question “Does this setTimeout always execute the function in exactly one second (1000 milliseconds) ?”
Call Stack
JavaScript does one thing at a time: only one function is executed at the same time. The Stack is the part that controls the execution order.
Every call to a function is sent to the Stack and executed. Let’s take a look at what happens in the previous example:
logOne
is sent to the Stack and executed.setTimeout(...)
is sent to the Stack and executed, but thelogTwo
inside is not run. We’ll see more on this later. But thesetTimeout
finishes and is removed from the Stack.logThree
goes to the stack and is then executed.After one second,
logTwo
appears in the stack and is executed.
Asynchronicity in Javascript
The Runtime Environment provides Javascript with a set of functionalities for “parallelizing” tasks. setTimeout
is one of those functionalities.
When we use these functionalities, the task that we are parallelizing is sent to some kind of background process. Once the background process finishes, it sends the function to a Message Queue. Finally, the functions are forwarded from the Message Queue to the Call Stack to be executed.
In the previous example, when setTimeout
is in the Stack, it sets a process in the background. When this process finishes, it sends logTwo
to the Message Queue, from which it will be moved to the Stack.
Event Loop
For logTwo
to be executed, it needs to be moved to the Stack. Yet, what if the Stack is full of other functions? Who decides to move something from the Queue to the Stack?
The answer is the Event Loop. The Event Loop is a simple program that does something similar to:
while (queue.waitForMessage() && stack.isEmpty()) {
queue.sendNextMessageToStack()
}
The Event Loop waits for messages and only moves the function to the Stack when it’s empty.
So, what happens if the Stack is not empty and there are messages in the Queue? They stay in the queue until the Stack is empty. Therefore, the functions are not executed immediately.
First Lesson and More
In a nutshell, setTimeout
sends logTwo
to the background and waits one second, then the background process sends the function to the Queue. But the function is not executed until the Stack is empty. So, logTwo
may take longer to run than one second.
We know that the background process sends logTwo
to the Queue in one second. Yet, that does not guarantee that it’s immediately forwarded to the Stack and executed.
setTimeout
is a guarantee to a minimum time of execution.
I wrote two gists that you can copy and paste into the console to check this.
Further Resources
Event Loop explained on MDN.
If you find this article slightly interesting, you will love this video of thirty minutes explaining the Event Loop. Web to see the Event Loop in action.
If you like this post, consider sharing it with your friends on twitter or forwarding this email to them 🙈
Don't hesitate to reach out to me if you have any questions or see an error. I highly appreciate it.
And thanks to Michal and Bernat for reviewing this article 🙏