JS event loop mechanism, fully explained from the browser process model, main thread working mode, various queue execution sequences, etc.

Table of contents

1. The browser process model

What is a process?

What is a thread?

What processes and threads does the browser have?

2. How does the rendering main thread work?

3. What is asynchronous?

4. How to understand the asynchrony of JS?

5. Why does JS hinder rendering?

6. Do tasks have priorities?

Execution order of test delay queue and interactive queue 

7. Can the timer in JS achieve accurate timing?  

8. Summary: What is the event loop of JS?

A single thread is what makes asynchrony happen. 

The event loop is an asynchronous implementation.

Nine, the case - look at the code, guess the result

1. The browser process model

What is a process?

 A program needs its own dedicated memory space to run, and this memory space can be simply understood as a process

What is a thread?

Once you have a process, you can run the program's code.

The "person" who runs the code is called a "thread".

A process has at least one thread, so after the process is started, a thread is automatically created to run the code, which is called the main thread.

If the program needs to execute multiple blocks of code at the same time, the main thread will start more threads to execute the code, so a process can contain multiple threads.

What processes and threads does the browser have?

The browser is a multi-process multi-threaded application

The internal work of the browser is extremely complicated. In order to avoid mutual influence and reduce the chance of serial crashes, when the browser is started, it will automatically start multiple processes.

You can view all current processes in the browser's task manager  

 

Among them, the most important processes are:

1. Browser thread: mainly responsible for interface display, user interaction, sub-process management, etc. Inside the browser process, multiple threads are started to handle different tasks

2. Network process: responsible for loading network resources. Multiple threads are started inside the network process to handle different network tasks

3. Rendering process: After the rendering process is started, a rendering main thread will be started, which is responsible for executing HTML, CSS, and JS codes. By default, the browser will start a new rendering process for each tab to ensure that different tabs Pages do not affect each other. The default mode may change in the future, please refer to the official chrome documentation

2. How does the rendering main thread work?

The main rendering thread is the busiest thread in the browser, and the tasks it needs to handle include but are not limited to:

  • parsing HTML
  • parsing CSS
  • calculation style
  • layout
  • work with layers
  • Draw the page 60 times per second
  • Execute global js code
  • Execute the event handler
  • Execute the callback function of the timer
  • ......

 To handle so many tasks, the main thread has encountered an unprecedented problem: how to schedule tasks?

for example:

  • I am executing a JS function, halfway through the execution, the user clicks the button, should I immediately execute the click event handler?
  • I'm executing a JS function, and a timer reaches the time in the middle of the execution, should I immediately execute its callback?
  • The browser process notifies me that "the user clicked the button", at the same time, a certain timer has also expired, which one should I handle?
  • ......

The rendering main thread came up with a brilliant idea to handle this: queuing 

1. At the very beginning, the rendering main thread will enter an infinite loop

2. Each cycle will check whether there is a task in the message queue. If there is, take out the first task to execute, and enter the next cycle after executing one; if not, enter the dormant state.

3. All other threads (including threads of other processes) can add tasks to the message queue at any time. New tasks are added to the end of the message queue. When adding a new task, if the main thread is dormant, it will be woken up to continue fetching tasks in a loop

In this way, each task can be carried out in an orderly and continuous manner.

The whole process is called event loop (message loop)

3. What is asynchronous?

During the execution of the code, some tasks that cannot be processed immediately, such as:

  • Tasks that need to be performed after the timing is completed—— setTimeout,setInterval
  • Tasks to be performed after the network communication is completed -- XHRFetch
  • Tasks to be performed after user operation --addEventListener

If the rendering main thread waits for the timing of these tasks to arrive, it will cause the main thread to be in a "blocked" state for a long time, causing the browser to "stuck"

The rendering main thread undertakes extremely important work, so it cannot be blocked anyway!

Therefore, browsers choose asynchronous to solve this problem

Using the asynchronous method, the rendering main thread will never block

4. How to understand the asynchrony of JS?

JS is a single-threaded language because it runs on the browser's main rendering thread, and there is only one rendering main thread.

The rendering main thread undertakes many tasks, rendering pages and executing JS are all running in it.

If you use a synchronous method, it is very likely that the main thread will be blocked, which will cause many other tasks in the message queue to be unable to be executed. In this way, on the one hand, the busy main thread will waste time in vain, and on the other hand, the page cannot be updated in time, causing the user to be stuck.

So the browser uses an asynchronous way to avoid it. The specific method is that when certain tasks occur, such as timers, networks, and event monitoring, the main thread will hand over the tasks to other threads for processing, and immediately end the execution of the tasks by itself, and then execute subsequent codes. When other threads are finished, wrap the callback function passed in advance into a task, add it to the end of the message queue, and wait for the main thread to schedule execution.

In this asynchronous mode, the browser never blocks, thus ensuring the smooth operation of the single thread to the greatest extent.

5. Why does JS hinder rendering?

Look at the code first

<body>
  <h1>Mr.Zhang is awesome!</h1>
  <button>change</button>
  <script>
    var h1 = document.querySelector('h1');
    var btn = document.querySelector('button');

    // 死循环指定的时间
    function delay(duration) {
      var start = Date.now();
      while (Date.now() - start < duration) { }
    }

    btn.onclick = function () {
      h1.textContent = '三秒后我才会变化!';
      delay(3000);
    };
  </script>
</body>

Execution effect:

mp4

Why doesn't clicking change immediately?

Trigger the click event. Although the code for modifying the text is executed immediately, it is found that the page needs to be drawn, and then it is packaged into a drawing task and placed in the task queue to wait for execution. The delay function specifies an infinite loop time of three seconds , the browser's main thread will not perform other tasks within these three seconds, and will wait for three seconds to complete this task before dispatching the drawing task from the queue for execution.

6. Do tasks have priorities?

Tasks have no priority, first in first out in the message queue

But the message queue has priority

According to the latest explanation from W3C:

  • Each task has a task type, tasks of the same type must be in one queue, and tasks of different types can belong to different queues. In an event loop, the browser can fetch tasks from different queues for execution according to the actual situation
  • The browser must prepare a micro-queue, and tasks in the micro-queue take precedence over all other tasks. HTML Standard

 As browsers grow in sophistication, the W3C no longer uses macro queues

 In the current chrome implementation, at least the following queues are included:

  • Delay queue: used to store callback tasks after the timer arrives, with priority "medium"

  • Interaction queue: used to store event processing tasks generated after user operations, with a priority of "high"

  • Micro-queue: the user stores the tasks that need to be executed the fastest, and the priority is "highest"

The main way to add tasks to the microqueue is to use Promise and MutationObserver  

For example:

// 立即把一个函数添加到微队列
Promise.resolve().then(函数)

Execution order of test delay queue and interactive queue 

Look at the code first:

<body>
  <button id="begin">开始</button>
  <button id="interaction">添加交互任务</button>
  <script>
    // 死循环指定的时间
    function delay(duration) {
      var start = Date.now();
      while (Date.now() - start < duration) { }
    }

    function addDelay() {
      console.log('添加延时队列');
      setTimeout(() => {
        console.log('延时队列执行');
      }, 0);
      delay(2000);
    }

    function addInteraction() {
      console.log('添加交互队列');
      interaction.onclick = function () {
        console.log('交互队列执行');
      };
      delay(2000);
    }

    begin.onclick = function () {
      addDelay();
      addInteraction();
      console.log('===========');
    };
  </script>
</body>

The addInteraction function is in the dead loop of the main thread for two seconds. If I click the Add Interaction Task button, the output will be like this

What does this mean? It shows that for Chrome, the user's interaction perception of the interface is more important, and the priority of the interaction queue is higher than that of the delay queue.

In the past, the only concept of microtask & macrotask could not explain the execution result of the above code at a certain time

7. Can the timer in JS achieve accurate timing?  

  1. Computer hardware does not have atomic clocks, so precise timekeeping cannot be achieved

  2. The timing function of the operating system itself has a small amount of deviation. Since the JS timer finally calls the function of the operating system, it also carries these deviations.

  3. According to the W3C standard, when the browser implements the timer, if the nesting level exceeds 5 layers, it will have a minimum time of 4 milliseconds, which will cause a deviation when the timing time is less than 4 milliseconds

  4. Affected by the event loop, the callback function of the timer can only run when the main thread is idle, so it brings a deviation

8. Summary: What is the event loop of JS?

The event loop, also known as the message loop, is the way the browser renders the main thread.

In the source code of Chrome, it starts an endless for loop, each loop fetches the first task from the message queue for execution, and other threads only need to add the task to the end of the queue at an appropriate time.

In the past, message queues were simply divided into macro queues and micro queues. This statement can no longer satisfy the complex browser environment, and a more flexible and changeable processing method has been replaced.

According to the official W3C explanation, each task has a different type, tasks of the same type must be in the same queue, and different tasks can belong to different queues. Different task queues have different priorities. In an event loop, the browser decides which queue to take. But the browser must have a micro-queue, and the tasks of the micro-queue must have the highest priority, and must be scheduled and executed first.

A single thread is what makes asynchrony happen. 

The event loop is an asynchronous implementation.

Nine, the case - look at the code, guess the result

setTimeout(function () {
  console.log(1);
}, 0);

function delay(duration) {
  var start = Date.now();
  while (Date.now() - start < duration) {}
}
delay(3000);
console.log(2);

Output 2, 1 after three seconds

function a() {
  console.log(1);
  Promise.resolve().then(function () {
    console.log(2);
  });
}
setTimeout(function () {
  console.log(3);
}, 0);

Promise.resolve().then(a);

console.log(4);

 Output in sequence: 4, 1, 2, 3

Guess you like

Origin blog.csdn.net/dabaooooq/article/details/129903606