Event Loop

Event Loop

JavaScript has been a single-threaded language since its inception. As for why it is designed to be single-threaded? Because the author of the language believed that it was only a scripting language executed in the browser, the requirements for its functionality were not very high. Early web pages did not have that high demand for JavaScript, they were all lightweight, and the programs that were required to be written must be simple, and if it involves multi-threading, the program logic will cause interaction and DOM operations to become very complicated.

The disadvantage of a single thread is that task execution will be blocked. The manifestation in the web page is: initiating data request --> http delay --> waiting for completion. During the waiting process, other operations cannot be performed, resulting in the page being unresponsive for a long time.

JavaScript uses asynchronous callbacks to solve these problems. The feature of the asynchronous callback is actually based on Event Loop.

1.1 JavaScript Engine and Runtime

To put it simply, in order to make JavaScript run, there are two parts to complete (of course, it is actually much more complicated than this):

The first part is Engine (JS engine): compile and execute JavaScript code, complete memory allocation, garbage collection, etc.;

The second part is Runtime: Provide some objects or mechanisms for JavaScript to enable it to interact with the outside world.

for example:

Both Chrome browser and Node.js use V8 Engine. V8 implements and provides all the data types, operators, objects, and methods in the ECMAScript standard (note that there is no DOM). But their Runtimes are different: Chrome provides Windows, DOM, and Node.js requires, process, and so on.

1.2 Browser thread

The JS engine is single-threaded, but the browser is multi-threaded. A tab of modern browsers, where threads include but are not limited to:

  • GUI rendering thread
  • JS engine thread
  • Event trigger thread
  • Timer trigger thread
  • Asynchronous http request thread

Asynchronous callbacks in JavaScript are supported through WebAPIs. Common ones include XMLHttpRequest, setTimeout, and event callbacks (onclik, onscroll, etc.). These API browsers all provide separate threads to run, so there will be a suitable place to handle the timing of the timer and the callback of various requests. That is, when these defined asynchronous tasks appear in the code, the browser implements the communication between them and the JS engine, which is not in the same thread as the JS engine.

In addition, GUI rendering and JavaScript execution are mutually exclusive. Although the two belong to different threads, the result of JavaScript execution may affect the page, so the browser handles this. In most cases, the JavaScript thread is executed, and the rendering thread will be suspended, waiting for JavaScript. The synchronization code is executed before rendering.

2 Definition of event loop

Event Loop is the core mechanism that enables JavaScript to be both single-threaded and absolutely non-blocking. It is also the basis of the JavaScript Concurrency Model. It is used to coordinate various events, user interaction, script execution, and UI. A mechanism for rendering, network requests, etc. The function of Event Loop is very simple: monitor the call stack and task queue , if the call stack is empty, it will take out the first "callback function" in the queue, and then push it to Call the stack, and then execute it.

In general, Event Loop is just a mechanism for implementing asynchronous callbacks.

2.1 There are two kinds

Event Loop is divided into two types, one exists in Browsing Context, and the other exists in Worker.

2.1.1 Browsing Context refers to an environment used to present the Document to the user. For example, tabs, windows, or iframes in browsers usually contain Browsing Context.

2.1.2 Worker refers to an API that is independent of UI scripts and can execute scripts in the background. It is often used to process some computationally intensive tasks in the background.

This article focuses on the Event Loop in Browsing Context, which is more complicated than the Event Loop in Worker.

In addition, Event Loop is not defined in the ECMAScript standard, but in the HTML standard. In the JS engine (take V8 as an example), it only implements the ECMAScript standard and does not care about the Event Loop. In other words, the Event Loop belongs to the JavaScript Runtime and is provided by the host environment (such as a browser). So don't get it wrong, this is the reason why JavaScript Engine and Runtime are introduced earlier.

2.2 Each "thread" has its own Event Loop. Therefore, each web worker has an independent Event Loop, and they can all run independently; Windows of the same origin share an Event Loop, and they can communicate with each other.

3 Memory model

From the perspective of the JavaScript memory model, we can divide the memory into call stack (Call Stack), heap (Heap) and task queue (Queue), and other parts:

image.png

3.1 Call stack The call stack will record all function call information. When we call a function, its parameters and local variables will be pushed onto the stack in the form of a stack frame (into the stack); after the execution is completed, the top of the stack will be popped up. frame. Let's take a look at the following example:

function multiply(x, y) {
  return x * y;
}
function printSquare(x) {
  var s = multiply(x, x);
  console.log(s);
}
printSquare(5);

When the program starts to execute, the call stack is empty. Then, the steps are as follows:

image-1.png

3.2 heap The heap stores a large amount of unstructured data, such as variables and objects allocated by the program.

3.3 Task queue The task queue contains a series of pending information and associated callback functions. The task queue is divided into two types: MacroTask Queue and MicroTask Queue.

4 MacroTask and MicroTask

4.1 MacroTask Queue Event Loop will have one or more MacroTask Queue, which is a first-in-first-out (FIFO) ordered list that stores Tasks (also known as MacroTasks) from different Task Sources.

Regarding Task, some people often call it MarcoTask colloquially, but in fact, there is no such saying in the HTML standard. However, in order to facilitate understanding, this book still uses the popular name MacroTask.

In the HTML standard, several common Task Sources are defined:

  • DOM manipulation(DOM manipulation);
  • User interaction (user interaction);
  • Networking (network request);
  • History traversal(History API operation )

The definition of MacroTask Source is very broad. Common mouse and keyboard events, AJAX, database operations (such as IndexedDB), and timer-related setTimeout, setInterval, etc. belong to Task Source. All MacroTask from these MacroTask Sources will be placed Waiting for processing in the corresponding MacroTask Queue.

For MacroTask, MacroTask Queue and Task Source, there are the following regulations:

  1. MacroTasks from the same Task Source must be placed in the same MacroTask Queue;

2.MacroTasks from different Task Sources can be placed in different MacroTask Queues;

3.The MacroTasks in the same MacroTask Queue are executed in order;

4.But for different MacroTask Queue (Task Source), the browser will schedule, allowing priority execution of MacroTask from a specific Task Source.

For example, mouse, keyboard events, and network requests have their own MacroTask Queue. When the two exist at the same time, the browser can prioritize and execute the MacroTask from the MacroTask Queue related to user interaction, such as mouse and keyboard events here, so as to ensure a Smooth user experience.