Understanding JavaScript Event Loop

We all know that there is such a concept as the Event Loop, but if we look into the specification, we won't find such a term or definition. This is quite simple: the Event Loop is not part of JS (ECMAScript) and, accordingly, is not regulated by the specification.

The concept of the Event Loop exists within the framework of the host, and different implementations of the JavaScript engine can use the Event Loop at their discretion as part of the environment API in which it is used.

But what is the official source for the Event Loop? It's already clear that the specification doesn't cover this because it's beyond the responsibility of the language (JS), but within the responsibility of the host that executes the JavaScript code. Therefore, the information about it should be sought somewhere that documents or regulates the environment's execution. However, there are many such environments today. They can be roughly divided into two groups: browser and non-browser.

Let's run through the browser environments

The mechanisms of such environments are governed by the WHATWG organization and the HTML specification. Specifically, the Event Loop is discussed in this section. But it's important to note that browsers operate with the operating systems' APIs where they run. For instance, Chromium on macOS uses NSRunLoop, while on Linux, it uses glib. However, in a cross-platform environment like Electron, there are certain complexities in implementing the Event Loop for different operating systems, so libuv is used, similar to Node.js.

Non-browser environments

Here, it’s clear that we won't be talking about WHATWG and the HTML specification. And because there are no standards or specifications other than ECMA-262 about how these environments should work, the only place to learn about them is their official documentation.

The most popular one now is Node.js. If you dig into the documentation, you can find the event loop section. It seems there's no other description. But it's immediately apparent that Node.js uses libuv. By the way, there’s also a designed diagram showing how it works. And there’s a guide you will never look at.

Recently, among the non-browser environments, there's Bun, aiming to become a standard in development this year, and Deno, which I have never tried. They also use libuv to work with the Event Loop.

React Native and NativeScript are also non-browser environments. They work with the API of the corresponding platform they run on. Android uses Android.os.Looper, and iOS uses RunLoop.

To wrap up the topic of the Event Loop, we understand that it doesn't fall within the responsibility of the JavaScript programming language. We can consider it an external service for JavaScript that helps execute JS code. Therefore, the Event Loop is responsible for many processes, such as input/output operations (mouse or keyboard events, file reading, etc.), rendering, and network operations.

If we try to describe what the Event Loop consists of, it's the task queue and the microtask queue. Tasks placed by the host go into the task queue, while the microtask queue is an optional feature for a task from the task queue to use the Web API for executing some asynchronous tasks.

I wrote these first two paragraphs, and it seems to me that the conclusion is a bit messy.

Let’s go, what goes into the task queue?

  • Events: Sending events goes here. Some events can be combined, such as MouseEvent and KeyboardEvent.
  • Callbacks: Like setTimeout(() => {}). Here, the callback inside the timeout will be a separate task.
  • Loading resources: An image of a cat will be a separate task.
  • DOM: If you add a new element to the DOM, it will repaint the parent element.

What about the microtask queue?

This is where you can organize asynchronous operations, such as Promise.

By the way, people say that the concept of macrotask doesn't exist in the specification.