V8
一、认识
一、执行宏任务: JavaScript
中初始同步执行的代码都被看作为一个宏任务(包括从事件队列中拿出的任务)。当主线程空闲时, 会从任务队列中取出一个宏任务(例如由 setTimeout
、XHR/Fetch
等异步操作触发的回调),然后压入调用栈执行。虽然大部分异步 API
(如 setTimeout
、XHR/Fetch
)产生的回调被归类为宏任务,但不同浏览器中可能会存在多个任务队列(例如用户交互事件、网络事件、定时器等),它们的调度策略和优先级可能有所不同。
二、添加微任务: 在宏任务执行过程中, 如果遇到微任务(如 Promise
的 .then
、MutationObserver
等),它们不会立即执行,而是被放入微任务队列中。
三、执行微任务: 每个宏任务结束后,都会检查并清空微任务队列。如果在执行微任务过程中又产生了新的微任务,这些新的微任务也会在当前事件循环内一并执行,而不会等待下一个宏任务。
四、页面渲染阶段: 当宏任务及其产生的所有微任务都执行完成后,浏览器进入渲染阶段。渲染机会(rendering opportunity
) 的判断条件包括:
-
当前页面是否需要更新(是否有视觉变化)
-
屏幕刷新率限制(如
60fps
或在低性能环境下可能降至30fps
甚至更低) -
页面是否处于可见状态(后台页面会降低渲染频率)
五、渲染阶段的细分步骤
-
执行
resize
回调, 若页面尺寸发生变化,浏览器会派发resize
事件。 -
执行
scroll
回调, 当页面滚动时,浏览器会派发scroll
事件。 -
执行
requestAnimationFrame
回调, 在重新渲染前,调用所有注册的requestAnimationFrame
回调,为动画修改DOM
提供最后时机,确保动画流畅。 -
执行
IntersectionObserver
回调, 处理视口变化检测任务。 -
重新渲染绘制用户界面, 真正更新页面的显示。
-
执行
requestIdleCallback
回调, 在执行栈和微任务队列均为空时,利用空闲时间执行一些低优先级任务。
六、下一轮事件循环: 一旦上面所有步骤完成, 事件循环回到第 1
步, 从任务队列中获取下一个宏任务继续执行。
二、任务
2.1 Macro-Task (宏任务)
-
I/O
-
UI 交互事件
-
setTimeout
-
setInterval
-
setImmediate
-
MessageChannel
2.2 Micro-Task (微任务)
-
Promise.then
-
Object.observe
-
process.nextTick
-
MutationObserver