头图

Hi everyone, I'm Kasong.

front-end framework that merges updates triggered by multiple independent variable changes into a at one time. Different types of frameworks have different batch processing timings.

For example, the following Svelte code, click H1 executed after onClick callback, triggered three updates. Due to batch processing, three updates will be merged into one.

Then print the rendering results in the form of synchronization, micro task, and macro task:

<script>
  let count = 0;
  let dom;
  const onClick = () => {
    // 三次更新合并为一次
    count++;
    count++;
    count++;
  
    console.log("同步结果:", dom.innerText);
  
    Promise.resolve().then(() => {
      console.log("微任务结果:", dom.innerText);
    });
  
    setTimeout(() => {
      console.log("宏任务结果:", dom.innerText);
    });
  }
</script>

<h1 bind:this={dom} on:click={onClick}>{count}</h1>

The same logic is implemented in different frameworks, and the printing results are as follows:

  • Vue3 : Synchronization result: 0 Micro task result: 3 Macro task result: 3
  • Svelte : Synchronization result: 0 Micro task result: 3 Macro task result: 3
  • Legacy React : Synchronization result: 0 Micro task result: 3 Macro task result: 3
  • Concurrent React : Synchronization result: 0 Micro task result: 0 Macro task result: 3
4 implementations of Demo address: React
Vue3
Svelte

The essential reason is: some frameworks use macrotasks to implement batch processing, and some frameworks use implement batch processing.

This article will explain the macrotask and , and their relationship with batch processing.

Welcome to join the human high-quality front-end framework group , take flight

How to schedule tasks

Put the complete flow chart first, so that you can get an overall impression:

事件循环流程图

By default, the browser (to Chrome for example) in each Tab corresponding to the page a rendering process, the rendering process contains the main thread, synthetic thread, IO multiple threads and other threads.

The work of the main thread is very busy, to deal with DOM , calculation style, processing layout, processing event response, execution JS etc.

There are two problems to be solved here:

  1. These tasks not only come from within the thread, but may also come from outside. How to schedule these tasks?
  2. How can new tasks participate in scheduling when the main thread is working?

The answer to the first question is: message queue

All tasks participating in the scheduling will be added to the task queue. According to first in first out , the task that enters the queue first will be processed first. The pseudo code is described as follows:

// 从任务队列中取出任务
const task = taskQueue.takeTask();
// 执行任务
processTask(task);

Other processes by IPC sends the task to the rendering process of IO threads, IO thread and then sent to the main thread task queue of tasks, such as:

  • After the mouse is clicked, the browser process IPC the "click event" to the IO thread through 06194693606243, and the IO thread sends it to the task queue
  • After the resource is loaded, the network process IPC the "load completion event" to the IO thread through 0619469360628d, and the IO thread sends it to the task queue

How to schedule new tasks

The answer to the second question is: event loop

The main thread will perform tasks in the loop statement. As the cycle continues, newly added tasks will be inserted at the end of the queue, and old tasks will be taken out for execution. The pseudo code is described as follows:

// 退出事件循环的标识
let keepRunning = true;

// 主线程
function MainThread() {
  // 循环执行任务
  while(true) {
    // 从任务队列中取出任务
    const task = taskQueue.takeTask();
    // 执行任务
    processTask(task);

    if (!keepRunning) {
      break;
    }
  }
}

Delayed task

In addition to the task queue, the browser also WHATWG standard to store tasks that need to be delayed (such as setTimeout ). The pseudo code is as follows:

function MainThread() {
  while(true) {
    const task = taskQueue.takeTask();
    processTask(task);

    //执行延迟队列中的任务 
    processDelayTask()

    if (!keepRunning) {
      break;
    }
  }
}

After the current round of cyclic tasks are executed (that is, after the execution of processTask processDelayTask will be executed to check whether there is a delayed task due, and if there is a task expired, it will be executed.

Between processDelayTask execution timing in processTask after, so when the task execution time is relatively long, it may cause delays task can not be executed on schedule. Consider the following code:

function sayHello() { console.log('hello') }

function test() { 
  setTimeout(sayHello, 0); 
  for (let i = 0; i < 5000; i++) {
    console.log(i);
  }
}
test()

Even if the delay time of the sayHello 0 , it needs to wait for test to be executed before it can be executed, so the sayHello is greater than the set time.

Macro task and micro task

New tasks added to the task queue need to wait for the other tasks in the queue to be executed before they can be executed, which is disadvantageous for sudden situation of 1619469360644a.

In order to solve the problem of timeliness, the tasks in the task queue are called macro tasks. During the micro tasks can be generated micro task queue in the task execution context.

That is, the right part of the flowchart:

事件循环流程图

In before the end of the macro task execution will traverse its micro task queue, the process macro task execution generated micro-task batch execution.

MutationObserver

How do microtasks solve the problem of timeliness while taking into account performance?

Consider for monitoring DOM changes in micro-task API - MutationObserver .

When multiple occurrences of the same macro task DOM change, will produce more MutationObserver micro tasks, the timing of its implementation before the end of the execution of the macro task, as compared to the new macro task into the queue waiting to be executed to ensure timeliness.

At the same time, because the DOM in the microtask queue are executed in batches, the performance is better than when the callback is executed synchronously every time 06194693606531 changes.

Summarize

The implementation batch processing in the framework is very similar in MutationObserver advantage of the asynchronous execution of 16194693606579 macrotasks and , package the updates and execute them.

It's just that different frameworks have different update granularity, such as Vue3 and Svelte update granularity is very fine, so used to realize batch processing.

React relatively coarse, but the internal implementation is more complicated, that is, there are scenes with macro tasks and scenes with micro tasks.


卡颂
3.1k 声望16.7k 粉丝