Trust issues with setTimeout()

Photo by Agê Barros on Unsplash

Trust issues with setTimeout()

If you have written js code for sometime then you have definitely come across something called "setTimeout()". It's quite a simple function, takes in a callback function as it's first argument and a duration as it's second. It then executes said callback function after the provided duration. Or does it? On the surface it seems quite simple, you pass in a function and it gets executed after the duration provided. The fun part is in how the function works under the hood.

Lets use this to understand why does something not execute immediately even when passing a 0 sec delay.

According to the MDN docs:

The global setTimeout() method sets a timer which executes a function or specified piece of code once the timer expires.

Basic usage syntax:

setTimeout(code, delay)

setTimeout() also returns a timeoutID, it is a positive integer value which identifies the timer created by the call to setTimeout(). This value can be passed to clearTimeout() to cancel the timeout.

Now to understand this better lets understand how JavaScript itself works.

JavaScript is Synchronous

By now you may know that JS is a synchronous language. Which basically means that there can only be one function at one time. But we always see so many asynchronous actions happening all the time right? Let's get a basic outline of how anything gets executed in JS. To do that let's start with the Event Loop.

The Event Loop

So, the Javascript runtime, V8 has a something called a heap and the call stack.

jsruntime.png

The heap is where all the memory allocation happens and the stack your execution context. Now anything you do, all your functions get executed from the call stack. However, something like setTimeout() doesn't even exist in V8. Which begs the question, how on earth does it even work?

In modern browsers V8 or similar runtime's work in tandem with something called web api's. In its essence Web API's are extra functions provided by the browsers to enhance it's functionalities. If you were to look at the full picture of the entire javascript environment, it would look something like this.

eventloop.png

These Web API's are what provides javascript it's asynchronous behavior.

Note that javascript is still synchronous. It just makes use of the web api's to execute asynchronous functions.

How does it all work?

When you write a simple function like say, to perform arithmetic operations on two numbers, lets say something like this:

function add(a, b){
    console.log(a+b);
}
add(6, 5);

Javascript first pushes a main() function onto the call stack. The main() function is the gateway into the program/script or basically the file itself in this context. It then pushes add() function to the stack followed by the console.log() function. The stack now looks something like this:

stack.png

Once it hits that console statement, it prints out the value and starts to pop the function off the call stack. The same happens with the add() function, once execution is over it gets popped off.

Stacks follow a LIFO structure. It means that the last element in would be the first element out. Putting something on the stack is called "push" and removing something is called "pop".

This is the basic flow when dealing with synchronous functions. Things get a bit complicated when we start adding async functions like setTimeout(), setInterval() etc.

Let's look at what happens with setTimeout()

Here's a simple function.

console.log('Outside before setTimeout');
setTimeout(function(){
    console.log('Inside setTimeout');
}, 2000);
console.log('Outside after setTimeout');

The output looks something like this:

op1.PNG

But let's say we have something like this:

console.log('Outside before setTimeout');
setTimeout(function(){
    console.log('Inside setTimeout');
}, 0);
console.log('Outside after setTimeout');

What do think happens then? Well, let's have a look shall we?

op2.PNG

Now that's strange. Why isn't the console inside the setTimeout fucntion printing first?

Enter, the event loop!

Behind the scenes, the setTimeout first gets pushed into the call stack just like before but now, the setTImeout function gets pushed into the web api thread. It's the web api that creates the timer. In out case its 2 seconds. The setTimeout call itself is now complete hence it gets popped off the stack. The last console log statement now gets pushed and then popped off after execution. In the meantime if the timer for the setTimeout runs out, the callback gets pushed into something called the task queue. It then waits in the task queue until the call stack is empty. Once it is empty, the callback gets pushed from the task queue to the main call stack and it then executes.

All the event loop does is, it observes both the call stack and the task queue and when the call stack is empty it pushes the the first callback in the task queue to the call stack.

So basically the flow would be something like this:

function call -> pushed to the call stack -> web api takes care of it -> pushed into the task queue once timer runs out -> event loop pushes the callback into the call stack if its clear -> gets executed like a normal js function.

This is the reason why functions don't execute immediately even if you pass in a 0 second delay. The event loop still has to wait for the call stack to be empty before it can push the callback onto it for execution.

Conclusion

  1. setTimeout() takes a callback and a delay.
  2. It is not a part of V8 but is provided by the web api's.
  3. It's execution gets taken care of by the event loop.
  4. It returns a timeoutID that can be used to clear a timeout.

Further reading:

  1. setTimeout MDN
  2. Event Loop

Lets connect over at Twitter, LinkedIn