The Timer is set. Now what?

setTimeout( ) and the mystical Event Loop

The Timer is set. Now what?

The following piece covers the basics of "timers" in JS. One can view it as a baby step towards async programming.

Enjoy 🤗

Time out

The setTimeout() function's purpose is to postpone the call to a function (callback), with the delay specified in milliseconds. We can also specify arguments to pass to the callback when it is called.

setTimeout(cb, delay, arg1, arg2...)

For instance, the code below prints some text after 2.5s:

setTimeout(fname => {
  console.log('Hello,', fname);
}, 2500, 'Ava');

It's not that simple!

setTimeout() is not a part of the V8 runtime, the JS engine used by Node.js and browsers such as Brave and Chrome.

Weird, isn't it?

Let's begin with a bit of housekeeping.

JavaScript is a single-threaded programming language, capable of doing only one thing at a time. It has a single call stack. (The call stack is a "data structure" used by the interpreter to keep track of its place in the program, such as which function is being executed at the moment)

Blocking

There is no formal definition for "blocking code"; it essentially refers to processes that are slow, like network requests, reading files from the hard disk, the setTimeout() function, etc.

Keeping such processes in the call stack is not effective. For example, the timer function mentioned above ⬆️ would block the stack for 2.5s. Nothing else would occur until the timer ends and the callback eventually returns. Similarly, a network request may or may not be fulfilled; it is illogical to keep the following blocks of code waiting until the server responds.

It poses an intriguing question: If not the call stack, then where?

Concurrency

The callback within setTimeout() is pushed onto the call stack after a delay. It means that the timer somehow works concurrently while some other code is running.

How?!

Well, it is true that the JS runtime can only do one thing at a time, but the environment, Node (the browser too), is more than just the runtime. ⚠️

They also have these APIs, which are practically threads that we can't directly access but can only make calls to. These additional components of the environment (Node or any browser) handle the "concurrency."

Note: A thread refers to another program running on a separate processor core. As most modern PCs have multiple cores, multiple threads can run simultaneously.

the Event Loop

I mentioned earlier that setTimeout() is not a part of V8; it is, in fact, an API provided by the environment.

So, when we call setTimeout() with a callback and a delay, the browser (or Node) starts a timer, i.e., a separate thread handles the countdown. It also indicates the completion of the timer function call, which, in turn, is popped off the stack.

Now what?

The Web APIs cannot just barge into the call stack whenever they are ready; otherwise, they would appear randomly in the middle of some other code.

In the case of setTimeout(), when the countdown ends, the API pushes our callback onto the event queue (also called task queue), a data structure following FIFO.

This is where the event loop chimes in, the final piece to the puzzle ⤵️

The event loop's job is to look at the call stack and the task queue. If the stack is empty, it takes the first callback in the queue and pushes it onto the stack, effectively running it.

An example

Here, when the API is done, the callback function is sent to the event queue, from where it's pushed onto the call stack only when it's free. The process is schematically shown below:

Multiple folks waiting...

Consider the snippet below.

How does the event loop handle multiple timeout blocks, each with different timers?

The different timer APIs work concurrently. When any of them is done, it is moved to the event queue. It's important to note that Web APIs do not adhere to a FILO or FIFO policy. However, the callbacks in the task queue are pushed onto the call stack one by one in a FIFO order.

Note: In case the waiting periods of different block collide, they are pushed onto the queue following FIFO.


Credits: Prash and Philip Roberts (JSConf EU 2014)

Thank you for reading 😊

See ya!