DEV Community

Cover image for Exploring the Inner Workings of the JavaScript Event Loop
YogiYiorgos
YogiYiorgos

Posted on

Exploring the Inner Workings of the JavaScript Event Loop

Introduction

Asynchronous programming is an essential part of modern web development. Whether you're making network requests to APIs, handling user input in the browser, or performing complex calculations, it's important to understand how JavaScript handles asynchronous tasks. One key concept in JavaScript's approach to asynchronous programming is the event loop.

The event loop is the mechanism that allows JavaScript to execute multiple tasks concurrently without blocking the main thread. In this blog post, we'll explore what the event loop is, how it works, and provide real-life examples of how it's used in JavaScript.

What is the event loop?

Specifically in Javascript, the event loop is a mechanism that allows the single threaded environment of the language to handle asynchronous operations. The event loop is a continuous process that runs in the background of JavaScript's execution environment, constantly monitoring a queue of tasks that need to be processed. When the main thread finishes executing a task, the event loop checks the task queue for any pending tasks and processes them in the order they were received. This allows JavaScript to execute multiple tasks concurrently, without blocking the main thread.

How does the event loop work?

In Javascript, there is only one main thread of execution that processes code in a linear fashion, one line at a time. When an asynchronous operation is initiated, such as a network request, it is handed off to a separate thread of execution provided by the browser, such as the Web API thread, to be processed in the background. The main thread then continues executing the next line of code, without waiting for the asynchronous operation to complete.

Once the asynchronous operation is completed, it is added to a queue of events that are managed by the event loop. The event loop constantly monitors this queue and, when the queue is not empty, it processes the events in a FIFO order.

The event loop uses a two-phase cycle to manage the queue of events. In the first phase, the event loop processes all synchronous events in the queue, such as mouse clicks and button presses. These events are typically processed quickly and are not likely to block the main thread. In the second phase, the event loop processes asynchronous events, such as callbacks from network requests, that have been added to the queue.

When the event loop processes an asynchronous event, it runs the associated callback function on the main thread. This can result in the callback being executed at any point in the program's execution, even if it is in the middle of processing another task. This is why callback functions must be designed to be non-blocking and lightweight to avoid blocking the main thread for too long.

Real-life examples

Let's look at some real-life examples of how the event loop is used in Javascript.

Example 1: Network request

When we make a network request in Javascript, the request is handled asynchronously by the browser's networking stack. This means that the main thread can continue executing code while the request is being processed in the background. Once the response is received, the callback function is added to the task queue, and the event loop processes it when it's the next item in the queue.

fetch('https://jsonplaceholder.typicode.com')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.log(error))
Enter fullscreen mode Exit fullscreen mode

Example 2: Timer

When we use the setTimeout function to schedule a timer in Javascript, the timer is handled asynchronously by the browser's timer API. This means that the main thread can continue executing code while the timer is counting down in the background. Once the timer expires, the callback function is added to the task queue, and the event loop processes it when it's the next item in the queue.

console.log("Starting")

// Set a timer to execute after 2 seconds
setTimeout(() => {
    console.log("Timeout 1")
}, 2000)

// Set another timer to execute after 1 second
setTimeout(() => {
    console.log("TImeout 2")
}, 1000)
console.log("Finishing")
Enter fullscreen mode Exit fullscreen mode

When this code is executed, the following happens:

  1. The first console.log statement is executed, logging "Starting" to the console.
  2. The first setTimeout call is made, scheduling the first callback function to be executed after 2 seconds. However, since this is an asynchronous operation, the main thread continues executing the next line of code without waiting for the timer to expire.
  3. The second setTimeout call is made, scheduling the second callback function to be executed after 1 second. Again, the main thread continues executing the next line of code without waiting for the timer to expire.
  4. The final console.log statement is executed, logging "Finishing" to the console.
  5. The event loop continues to run on the main thread, monitoring the queue of events.
  6. After 1 second has elapsed, the first timer expires and adds the associated callback function to the event queue.
  7. The event loop processes the first event in the queue, which is the callback function from the first setTimeout call, logging "Timeout 2" to the console.
  8. After another second has elapsed, the second timer expires and adds the associated callback function to the event queue.
  9. The event loop processes the second event in the queue, which is the callback function from the second setTimeout call, logging "Timeout 1" to the console.

The console prints:

Starting
Finishing
Timeout 2
Timeout 1
Enter fullscreen mode Exit fullscreen mode

Top comments (0)