Macro tasks, micro tasks, event loop event loop and promise, setTimeout, async, nextTick [Super detailed example explanation]

Table of contents

js single thread

Macro tasks: tasks queued for execution on the main thread, executed sequentially

Macro task macrotask: setTimeout, setInterval timing events, Ajax, DOM events, script execution, I/O operations, UI rendering, etc.

Microtasks: tasks that do not enter the main thread but enter the "microtask list"

Microtask (asynchronous): Promise, async/await, alert (high priority)

event polling mechanism

0. During the execution of macro tasks, when micro tasks are encountered, they are added to the micro task queue in turn.

1. After the current macro task is executed, it will be determined whether there are tasks in the micro task list.

2. If so, all microtasks will be put into the main thread and executed

3. If not, continue to the next macro task

Repeat the above loop

setTimeout(delay=0)=setImmediate: Next Event Loop execution

async, await event polling execution timing

async implicitly returns Promise, which will generate a microtask await xx; the following code will be executed during the microtask

event loop and browser update rendering timing

Macro task → Micro task → Rendering update

When there are a large number of macro tasks, you can change DOM->Micro tasks

Vue asynchronous update principle

nextTick: Execute callback function after DOM update

process.nextTick in Node

timer

setTimeout is executed after a fixed duration and setInterval is executed repeatedly at a fixed interval. The minimum duration of setTimeout and setInterval is 4ms.

Reasons why the timer is inaccurate

The execution time required for setTimeout/setInterval is not certain

setTimeout/setInterval Animation stuck: refresh frequency ≠ time interval

requestAnimationFrame: API provided by the browser specifically for animation

The difference between setTimeout, setInterval and requestAnimationFrame

1) Engine level

setTimeout: JS engine, event polling requestAnimationFrame exists. It belongs to GUI engine. JS engine and GUI engine are mutually exclusive: GUI engine will block the calculation of JS engine during rendering.

2) Performance level

When the page is hidden or minimized: the timer setTimeout will still perform animation tasks in the background

When the page is in an inactive state, the screen refresh task of the page will be suspended by the system: requestAnimationFrame will also stop


js single thread

Macro tasks: tasks queued for execution on the main thread, executed sequentially

Macro task macrotask: setTimeout , setInterval scheduled events, Ajax, DOM events, script execution,  I/O operations, UI rendering etc.


Microtasks: tasks that do not enter the main thread but enter the "microtask list"

Microtask ( asynchronous ): Promise, async/await, alert (high priority)

event polling mechanism

0. During the execution of macro tasks , when micro tasks are encountered, they are added to the micro task queue in turn.

1. After the current macro task is executed, it will be determined whether there are tasks in the micro task list.

2. If so, all microtasks will be put into the main thread and executed

3. If not, continue to the next macro task

Repeat the above loop

setTimeout(delay=0)=setImmediate: nextEvent Loop执行

//宏任务队列:[]
//微任务队列:[promise0]
Promise.resolve()
  .then(function() {
    console.log("promise0");
  })
  .then(function() {
    console.log("promise5");
  });
//定时的setTimeout(delay=0)=setImmediate:下个Event Loop执行
//宏任务队列:[timer1]
//微任务队列:[promise0]
setTimeout(() => {
  console.log("timer1");
  
  Promise.resolve().then(function() {
    console.log("promise2");
  });
  Promise.resolve().then(function() {
    console.log("promise4");
  });
}, 0);
//宏任务队列:[timer1,timer2]
//微任务队列:[promise0]
setTimeout(() => {
  console.log("timer2");
  Promise.resolve().then(function() {
    console.log("promise3");
  });
}, 0);
//宏任务队列:[timer1,timer2]
//微任务队列:[promise0,promise1]
Promise.resolve().then(function() {
  console.log("promise1");
});
//执行start
console.log("start");
//执行当前所有微任务队列:[promise0,promise1]
//执行promise0时将promise5放入了微任务队列:[promise1,promise5]
//接着执行微任务队列:输出promise1,promise5
//当微任务队列为空,开始执行宏任务队列[timer1,timer2]队首的timer1
//执行timer1时碰到了微任务promise2,放进微任务队列[promise2]
//宏任务timer1执行完了,开始执行所有当前所有微任务:[promise2]
//执行promise2完碰到微任务promise4,放进微任务队列:[promise4]
//当前微任务队列不为空,接着执行promise4
//微任务队列为空,接着执行宏任务队列队首[timer2]
//执行timer2时碰到了微任务promise3,放进微任务队列[promise3]
//宏任务timer2执行完了,开始执行所有当前所有微任务:[promise3]


// 打印结果: start promise0 promise1 promise5 timer1 promise2 promise4 timer2 promise3

async, await event polling execution timing

async implicitly returns Promise, which will generate a microtask
await xx; the following code will be executed during the microtask


//1.script start(同步)
console.log("script start");

async function async1() {
  await async2(); // await 隐式返回promise
  console.log("async1 end"); // 这里的执行时机:在执行微任务时执行
}

async function async2() {
  console.log("async2 end"); // 这里是同步代码
}
//2.async2 end(同步)
//微任务队列:[async1 end]
async1();
//宏任务队列:[setTimeout],setTimeOut进入下一loop
setTimeout(function() {
  console.log("setTimeout");
}, 0);
//3.Promise(同步)
//宏任务队列:[setTimeout]
//微任务队列:[async1 end,promise1]
new Promise(resolve => {
  console.log("Promise"); // 这里是同步代码
  resolve();
})
  .then(function() {
    console.log("promise1");
  })
  .then(function() {
    console.log("promise2");
  }); 
//4.script end(同步)
console.log("script end");
//当前loop的宏任务(都是同步代码)都执行完毕
//执行所有微任务[async1 end,promise1]
//执行promise1完后碰到了promise2,加入微任务队列,接着执行
//当前所有微任务都执行完毕,开始执行宏任务队列[setTimeout]

// 打印结果:  script start => async2 end => Promise => script end => async1 end => promise1 => promise2 => setTimeout

event loop and browser update rendering timing

宏任务 → 微任务 → 渲染更新

Browser update rendering will be performed after the macro tasks and micro tasks in the event loop are completed, that is 宏任务 → 微任务 → 渲染更新(macro tasks first, then micro tasks, then rendering updates)

When there are a large number of macro tasks, you can change DOM->Micro tasks

In the macro task queue, if there are a large number of tasks waiting to be executed, dom的变动作为微任务,能更快的将变化呈现给用户the DOM can be updated in this event polling.

Vue asynchronous update principle

1. If the same one  is triggered multiple times , it will only be pushed into the update queue once, which can avoid repeated modification of the same DOM. This removal of duplicate data is very important to avoid unnecessary calculations and DOM operations. watcher

2. The synchronization task is completed

3. Start executing tasks in the asynchronous watcher queue and update the DOM at once

// 定义watcher类
class Watcher {
  update() {
    // 放到watcher队列中,异步更新
    queueWatcher(this);
  }
  // 触发更新
  run() {
    this.get();
  }
}

// 队列中添加watcher
function queueWatcher(watcher) {
  const id = watcher.id;
  // 先判断watcher是否存在 去掉重复的watcher
  if (!has[id]) {
    queue.push(watcher);
    has[id] = true;
    if (!pending) {
      pending = true;
      // 使用异步更新watcher
      nextTick(flushSchedulerQueue);
    }
  }
}

let queue = []; // 定义watcher队列
let has = {}; // 使用对象来保存id,进行去重操作
let pending = false; // 如果异步队列正在执行,将不会再次执行

// 执行watcher队列的任务
function flushSchedulerQueue() {
  queue.forEach((watcher) => {
    watcher.run();
    if (watcher.options.render) {
      // 在更新之后执行对应的回调: 这里是updated钩子函数
      watcher.cb();
    }
  });
  // 执行完成后清空队列 重置pending状态
  queue = [];
  has = {};
  pending = false;
}

nextTick: Execute callback function after DOM update

Source code implementation of vue nextTick, asynchronous priority judgment, the summary isPromise > MutationObserver > setImmediate > setTimeout

MutationObserver is a Web API that allows developers to monitor DOM tree changes and execute callback functions when these changes occur

  1. Promise : If the browser supports it Promise, nextTickit will be used first Promise.thento create microtasks to ensure that the callback function is executed in the next microtask queue.

  2. MutationObserver : If the browser does not support it Promise, nextTickit will check whether it is supported MutationObserver. MutationObserverAllows monitoring of DOM tree changes, so it can also be used for scheduling asynchronous tasks. nextTickWill try to MutationObservercreate microtasks using.

  3. setImmediate : If the browser neither supports Promisenor supports it MutationObserver, nextTickit will check whether it is supported setImmediate. setImmediateIs a macrotask that usually setTimeoutexecutes earlier, so it is used to create asynchronous tasks at the macrotask level.

  4. setTimeout : If none of the above methods are available, nextTickit will fall back to using it setTimeoutto create an asynchronous task. setTimeoutIt is a macro task, but has a lower priority and may be executed after other asynchronous tasks.

In Vue 2.x, nextTickthe implementation mainly relies on macro tasks (such as setTimeout) and micro tasks (such as Promise)

Vue 3 takes advantage of new JavaScript features Promiseand microtasks to implement asynchronous task scheduling.

// 定义nextTick的回调队列
let callbacks = [];

// 批量执行nextTick的回调队列
function flushCallbacks() {
  callbacks.forEach((cb) => cb());
  callbacks = [];
  pending = false;
}

//定义异步方法,优先使用微任务实现
let timerFunc;

// 优先使用promise 微任务
if (Promise) {
  timerFunc = function () {
    return Promise.resolve().then(flushCallbacks);
  };
  // 如不支持promise,再使用MutationObserver 微任务
} else if (MutationObserver) {
  timerFunc = function () {
    const textNode = document.createTextNode('1');
    const observer = new MutationObserver(() => {
      flushCallbacks();
      observer.disconnect();
    });
    const observe = observer.observe(textNode, { characterData: true });
    textNode.textContent = '2';
  };
  // 微任务不支持,再使用宏任务实现
} else if (setImmediate) {
  timerFunc = function () {
    setImmediate(flushCallbacks);
  };
} else {
  timerFunc = function () {
    setTimeout(flushCallbacks);
  };
}

// 定义nextTick方法
export function nextTick(cb) {
  callbacks.push(cb);
  if (!pending) {
    pending = true;
    timerFunc();
  }
}

process.nextTick in Node

process.nextTick execution order is earlier than microtask

console.log("start");
//定时进入下一loop,宏任务队列:[timeout]
setTimeout(() => {
  console.log("timeout");
}, 0);
//微任务队列:[promise]
Promise.resolve().then(() => {
  console.log("promise");
});
//process.nextTick在微任务队首
//微任务队列:[nextTick,promise]
process.nextTick(() => {
  console.log("nextTick");
  Promise.resolve().then(() => {
    console.log("promise1");
  });
});
console.log("end");
// 执行结果 start end nextTick  promise promise1 timeout 

timer

setTimeout is executed after a fixed duration and setInterval is executed repeatedly
at a fixed interval . The minimum duration of setTimeout and setInterval is 4ms.

Reasons why the timer is inaccurate

The execution time required for setTimeout/setInterval is not certain

setTimeout/setInterval are macro tasks. According to the event polling mechanism, other tasks will block or delay the execution of js tasks.

Consider extreme cases. If the code in the timer requires a lot of calculations or DOM operations, and the code execution time exceeds the timer, the timer will be inaccurate.

setTimeout/setInterval Animation stuck: refresh frequency ≠ time interval

The screen refresh frequency of different devices may be different. setTimeout/setInterval can only set a fixed time interval. This time and the screen refresh interval may be different.

setTimeout/setInterval continuously changes the image to achieve animation effects by setting an interval. Causing, jittering, etc. may occur on different devices.

requestAnimationFrame: API provided by the browser specifically for animation

The refresh frequency of requestAnimationFrame is consistent with the refresh frequency of the display. Using this API can avoid animation freezes caused by setTimeout/setInterval.

requestAnimationFrame: Tells the browser to execute the passed callback function before the next redraw (usually a function that manipulates dom and updates animation)

The difference between setTimeout, setInterval and requestAnimationFrame

1) Engine level

setTimeout:  JS引擎, event polling
requestAnimationFrame exists GUI引擎
JS引擎与GUI引擎and is mutually exclusive: the GUI engine will block the calculation of the JS engine during rendering

The reason for this design is that if JS changes the dom at the same time during GUI rendering , it will cause the page rendering to be out of sync.

2) Performance level

When the page is hidden or minimized: the timer setTimeout will still perform animation tasks in the background
When the page is in an inactive state, the screen refresh task of the page will be suspended by the system: requestAnimationFrame will also stop

Guess you like

Origin blog.csdn.net/qq_28838891/article/details/133053470