DEV Community

Cover image for Untangling the JavaScript Event Loop
Joanna
Joanna

Posted on

Untangling the JavaScript Event Loop

JavaScript is single-threaded AND it's used asynchronously. This idea used to boggle my mind...until I learned about browser event loops.

JavaScript under the hood

First, we should understand the context in which our code is executed, or the runtime environment. You can think of this as the abstract container where the browser, the JS engine, web APIs, and your code all interact.

So while it's true that JavaScript is single-threaded, and that the engine interprets it line by line, there are other tools at play, and these tools help make using JS asynchronously possible. The event loop is simply another tool in this environment.

So what's the event loop?

Broadly, the event loop is a process the browser undergoes whenever it’s sent asynchronous code. It handles the timing of the execution of various chunks of the program.

To do that, and to see what needs to be done when, it monitors the function callstack and the callback queue. Whenever it's time to execute some code in the queue, the browser calls upon a JavaScript engine to perform that execution.

Event listener hash

One major part of the event loop is the event listener hash. This is basically a storage object for asynchronous functions to go after they've been parsed by the interpreter. There, they wait to be invoked. How long? Depends. But these functions are not touched until after the interpreter is finished running through the entire code body.

Callback queue

The callback queue is another key part of the event loop. Functions waiting in the event hash are eventually sent to the queue if its associated event has happened (time passing for setTimeout, or a click event, or a server response for an AJAX call, etc)...but again, only after the interpreter is done running through the code.

Tick, tick, tick

Each event loop, or tick, has three steps.

  1. First, the event loop performs some internal upkeep, which could be something like removing expired events from the events listener hash.
  2. If and only if the callstack is empty, it moves the appropriate functions from the events listener hash to the callback queue.
  3. The final step is to dequeue the first function from the queue and invoke it. And then the loop starts over gain.

The event loop in action

here's some mostly asynchronous code

Let's walk through how the above code would run with the event loop and the queue.

Engine interprets

First, the engine runs through the code line by line. If it sees any asynchronous functions, it passes them off to the event listener hash. In our example, the functions on lines 6, 10, and 13 all are handled asynchronously (by an event click, a setTimeout, and an AJAX call respectively), so they go wait in the hash.

The only function that actually gets invoked by the engine is logMeNow on line 19.

Browser examines hash

Once the engine is done interpreting, the browser can turn its attention to the event listener hash. It moves whatever functions it can to the callback queue (step two in the event loop). For now, this would include the phoneCall from line 9, since it had a wait time of 0 milliseconds.

We’re also gonna hypothetically say a user clicked on the body at this point, so we’ll queue up the function from line 6 too. And let's say we haven’t gotten our AJAX response yet from our request on line 13.

Browser moves to queue

The browser has now added everything to the queue that it needs to, so it can move onto step three of the event loop, which is dequeuing and invoking the function at the front of the queue.

The phoneCall from the setTimeout would be dequeued and invoked, and the click event phoneCall would be pushed to the top of the queue.

Back to the beginning

The loop then starts over with step one, the browser's internal upkeep. This would include removing the setTimeout phoneCall from the hash. (The event click function (lines 5-7) would remain in the hash, though-- click events and similar events remain in the hash throughout the life of a program.)

Voilà!

Hopefully this helped to clear up some confusion you may have had about the event loop. To recap, it's a tool the browser uses to handle asynchronous functions. It does that by storing those async functions in the event listener hash until (1) the interpreter is done running through the code and (2) the callstack is empty. When it's time, it moves functions whose 'events' have occurred from the hash to the queue, where they're invoked one at a time.

Top comments (0)