Understanding the Event Loop through setTimeout

This is some text inside of a div block.

One of my favorite features of JavaScript is its event loop and how it handles concurrency given JavaScript’s single-threaded nature. Specifically, there’s a lot of different implementations of JavaScript engines, but I’ll be referring to Google’s open-source V8 engine(which is written in C++ if you’re curious) that provides the runtime environment for Google Chrome.

In short, single-threaded == single call stack, and by virtue of being single-threaded, JavaScript can only handle running one line of code at a time (unless you’re using Web Workers, which create separate threads of execution independently of the thread that has access to the UI). If you’re running something that can take a long time (e.g. a network request, or a for loop with 1000 loops to run) the behavior is considered blocking. This means that JavaScript won’t run any further lines until the current process has finished.

The purpose of the Event Loop is to circumvent blocking behavior through using asynchronous callbacks, and allows us to queue asynchronous calls in an event queue while the call-stack is run to completion.

It’s a clever way to handle concurrency and allow us to run through synchronous operations while waiting for our asynchronous operations to load into our event queue and fire off a callback. These callbacks are loaded onto our event queue and fire off individually after the call stack is empty.

I’m using a tool called Loupe that allows us to go through each line and see exactly when our code is being executed synchronously in the call stack while queuing asynchronous callbacks in our event table, and subsequently our event queue. (In this case, the site refers to it as a WebAPI since setTimeout is an API provided to us, rather than actual being a function in the V8 source code).

Take this common interview question here using setTimeout. setTimeout will serve as our demonstration of registering asynchronous callbacks and demonstrate how JavaScript can handle concurrency while being single-threaded.

for (var i = 0; i<5; i++){  
   setTimeout(function(){console.log(i)},0);
}

What is logged out? Take a minute to think about what’s going on, and after you’ve finished hypothesizing what happens, let’s break down on how to approach this below.

omgrkzw

The call stack will run through synchronous lines of code, but in our for loop we’re actually running setTimeout on every iteration of our loop.

Every time we run setTimeout, we are:

  1. Putting that setTimeout call into our Web APIs/Event Table
  2. Waiting until the time allotted for the setTimeout passes
  3. Moving it into our Event/Callback Queue after the time passes.
  4. Checking to see if our call stack is empty, and if it is, then load the first callback in our event queue on to the call stack. If the call stack still has functions in it, run them all to completion.
  5. Run the previously loaded callback to completion until the call stack is clear again.
  6. Repeat steps 4 & 5 until there are no more functions in the Event Queue.

What ends up happening is that we run setTimeout on i=0, i=1, i=2, i=3, and i=4, and finally reach i=5, at which point we break out of the loop. Now, i is equal to 5, and our asynchronous callbacks are loaded in our Event Table and loaded into our Event Queue once the time has passed for every setTimeout. Each function runs individually, and since each function logs i, they will each log 5.

In this case, setTimeout(function(){…},0) means that we run the callback as soon as possible after all of our synchronous code has been executed. We’ll have five queued callbacks, and we’ll have five anonymous functions that all log (5). For a visual representation, click here.

Finally, if we had wanted to console.log(i) in order from 0 to 4, we could wrap it in a function like this:

for (var i = 0; i < 5; i++){  
  (function (idx){
     setTimeout(function(){
        console.log(idx);
     },idx * 1000);
  }(i))
}

Monthly Newsletter
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.