setTimeout function. Let’s look at a simple example
The above code prints “Hello” onto the console after a delay of 1000 milliseconds (1 second). Sounds simple enough, right? Let us now tweak the code a bit.
The above code will print “Hello” onto the console after a delay of 0 seconds. This means that it will print it instantly. How about we add some code after the
The above code should print out “Hello” and then print “World”, right? From what we have seen about the call stack, the
setTimeout function at line 1 is supposed to go into the call stack first, followed by the console.log function at line 5. But let’s look at the actual output
Output: World Hello
We see that “World” is printed before “Hello”. This means that the console statement at line 5 got executed before the
setTimeout function. How is that possible? It’s possible because the
setTimeout function never went into the call stack. Only the console.log statement at line 5 was sent into the call stack and got executed.
But we see that the
setTimeout function too eventually got executed. This is because the
Before we look into the callback queue, let’s understand a few things about the
setTimeout function. The first thing we need to know that
When an asynchronous function like
We have reached the end of the program
There are no functions left to be executed in the call stack
The above flow might sound a bit confusing to grasp at first. Let us try to understand it better with the help of an example.
In the above code, we have created a function blockThreadFor1Sec. Let us assume that it contains some code that takes approximately 1 second to run, for e.g. a for loop that is looped a billion times. When the loop finishes, the function then prints “1 second elapsed” onto the console.
At the beginning of the program, both the call stack and the callback queue are empty. Let us also take note of the timestamp at each step. Currently, it is at 0 ms
Timestamp: 0 ms | | | | | | | | | | |_______________| Call stack | | | | | | | | | | | | Callback queue
In line 1, the program only defines the function block1Second. The program then moves to line 6, where let’s say we’re at a timestamp of 1 ms (this isn’t the accurate timestamp, but just a rough value we take for simplicity). The program calls the
Timestamp: 1 ms | | | | | | | | | | |_______________| Call stack | | | | | | | | | setTimeout() | | | Callback queue
When the event loop is fired, it sees that the call stack is empty. It then looks at the callback queue and finds it non-empty with the
setTimeout function at the head. But it doesn’t immediately execute it because the function is set to execute only after a delay of 1000 ms. So, in our case, the function is to be executed only at a timestamp of (1 + 1000) = 1001 ms. Hence, the code inside the
setTimeout function isn’t called yet.
The program then moves to line 10, at which point let’s say we’re at a timestamp of 2 ms. The block1Second function is called and since it’s a normal synchronous function, it is added onto the call stack.
Timestamp: 2 ms | | | | | | | | | block1Second()| |_______________| Call stack | | | | | | | | Scheduled to | setTimeout() | -> execute at | | 1001 ms Callback queue
When the event loop gets fired, it sees that the call stack is non-empty. Hence, it executes the function at the top of the stack, which is block1Second. This function would take approximately 1 second or 1000 milliseconds to execute. Hence, when its execution is finished, we should be at a timestamp of (2 + 1000) = 1002 ms.
Here’s where things get interesting. As we have seen before, the
setTimeout function was scheduled to be executed at a timestamp of 1001 ms. So, when the event loop is fired at a timestamp of 1001 ms, the
setTimeout function present in the callback queue is not called yet because of condition #2 mentioned above that needs to be fulfilled first. i.e. the call stack needs to be empty. The call stack becomes empty only at 1002 ms when the block1Second function has finished executing and is removed from the call stack.
Let us now look at what happens at a timestamp of 1002 ms. The block1Second function finishes executing, “1 second elapsed” gets printed onto the console and the function is removed from the call stack.
Timestamp: 1002 ms | | | | | | | | | | |_______________| Call stack | | | | | | | | Scheduled to | setTimeout() | -> execute at | | 1001 ms Callback queue
Now that the call stack is empty, one might assume that the
setTimeout function is ready to be called the next time the event loop is fired. However, that is not the case as condition #1 mentioned above has not been fulfilled. i.e. we haven’t reached the end of the program yet. Hence, the program moves on in its execution without executing the
At line 12, we’re at a timestamp of 1003 ms. The program calls the console.log statement, which gets added to the call stack since it’s synchronous.
Timestamp: 1003 ms | | | | | | | | | console.log() | |_______________| Call stack | | | | | | | | Scheduled to | setTimeout() | -> execute at | | 1001 ms Callback queue
When the event loop is triggered, it sees that the call stack is non-empty with a single function. Hence, the console.log function is executed (which prints “World” onto the console) and then removed from the call stack. We have now reached the end of the program and are at a timestamp of 1004 ms.
Timestamp: 1004 ms | | | | | | | | | | |_______________| Call stack | | | | | | | | Scheduled to | setTimeout() | -> execute at | | 1001 ms Callback queue
When the event loop is now triggered, it sees that the call stack is empty. It also sees that the end of the program has been reached. Now that both the conditions have been fulfilled, the event loop finally is ready to move on to the callback queue to start executing functions from there. It sees that the callback queue is non-empty. Hence, it executes the function at the head of the queue, which is our
setTimeout function. The function prints “Hello” onto the console, after which the function reaches its end of execution and is removed from the callback queue.
Timestamp: 1005 ms | | | | | | | | | | |_______________| Call stack | | | | | | | | | | | | Callback queue
When the event loop is triggered again, it sees that the call stack is empty, the program has reached its end and the callback queue is also empty. Hence, the program is finally terminated.
This post was originally published here on Medium.