跳到主要内容

V8

2024年04月07日
柏拉文
越努力,越幸运

一、认识


一、执行宏任务: JavaScript 中初始同步执行的代码都被看作为一个宏任务(包括从事件队列中拿出的任务)。当主线程空闲时, 会从任务队列中取出一个宏任务(例如由 setTimeoutXHR/Fetch 等异步操作触发的回调),然后压入调用栈执行。虽然大部分异步 API(如 setTimeoutXHR/Fetch)产生的回调被归类为宏任务,但不同浏览器中可能会存在多个任务队列(例如用户交互事件、网络事件、定时器等),它们的调度策略和优先级可能有所不同。

二、添加微任务: 在宏任务执行过程中, 如果遇到微任务(如 Promise.thenMutationObserver 等),它们不会立即执行,而是被放入微任务队列中。

三、执行微任务: 每个宏任务结束后,都会检查并清空微任务队列。如果在执行微任务过程中又产生了新的微任务,这些新的微任务也会在当前事件循环内一并执行,而不会等待下一个宏任务。

四、页面渲染阶段: 当宏任务及其产生的所有微任务都执行完成后,浏览器进入渲染阶段。渲染机会(rendering opportunity 的判断条件包括:

  1. 当前页面是否需要更新(是否有视觉变化)

  2. 屏幕刷新率限制(如 60fps 或在低性能环境下可能降至 30fps 甚至更低)

  3. 页面是否处于可见状态(后台页面会降低渲染频率)

五、渲染阶段的细分步骤

  1. 执行 resize 回调, 若页面尺寸发生变化,浏览器会派发 resize 事件。

  2. 执行 scroll 回调, 当页面滚动时,浏览器会派发 scroll 事件。

  3. 执行 requestAnimationFrame 回调, 在重新渲染前,调用所有注册的 requestAnimationFrame 回调,为动画修改 DOM 提供最后时机,确保动画流畅。

  4. 执行 IntersectionObserver 回调, 处理视口变化检测任务。

  5. 重新渲染绘制用户界面, 真正更新页面的显示。

  6. 执行 requestIdleCallback 回调, 在执行栈和微任务队列均为空时,利用空闲时间执行一些低优先级任务。

六、下一轮事件循环: 一旦上面所有步骤完成, 事件循环回到第 1 步, 从任务队列中获取下一个宏任务继续执行。

二、任务


2.1 Macro-Task (宏任务)

  • I/O

  • UI 交互事件

  • setTimeout

  • setInterval

  • setImmediate

  • MessageChannel

2.2 Micro-Task (微任务)

  • Promise.then

  • Object.observe

  • process.nextTick

  • MutationObserver

参考资料


Node.js 事件循环机制

「进击的前端工程师」一文带你搞懂JavaScript事件循环

又被node的eventloop坑了,这次是node的锅

一次弄懂Event Loop

「前端进阶」从多线程到Event Loop全面梳理

深入解析你不知道的 EventLoop 和浏览器渲染、帧动画、空闲回调(动图演示)