resizeobserver 回调在微任务之后、宏任务之前执行,属于渲染前专用阶段;它批量处理尺寸变化、不响应同步重入、无法用 queuemicrotask 插入,且执行时机绑定浏览器 layout/paint 流程。

ResizeObserver 的回调由事件循环在 微任务队列之后、宏任务之前 的一个特殊阶段执行,属于“ResizeObserver 微任务阶段”,但严格来说它既不是 Promise.then 那样的标准微任务,也不是 setTimeout 那样的宏任务。
ResizeObserver 回调的执行时机
浏览器在每次渲染前(即 layout 之后、paint 之前)会检查 ResizeObserver 监听的目标尺寸是否发生变化。如果变化存在,相关回调会被加入一个**专用的观察者回调队列**,并在当前 JS 执行栈清空后、下一个宏任务开始前,被统一调用。
这个过程大致顺序是:
- 同步代码执行完毕
- 所有已排队的 Promise 微任务(如 then/catch)执行
- ResizeObserver 回调批量执行(按注册顺序,且仅触发一次/帧)
- 其他观察者回调(如 MutationObserver)执行(注意:MutationObserver 在 ResizeObserver 之前)
- 浏览器进行渲染(layout → paint)
- 下一个宏任务(如 setTimeout)运行
为什么 ResizeObserver 不是标准微任务?
尽管它在微任务之后立即执行,但规范中明确将其与 Promise 微任务区分开。关键区别包括:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 无法通过
queueMicrotask()手动插入到该阶段 - 回调总是批量合并执行:同一帧内多次尺寸变化只触发一次回调,且传入的
entries包含所有待处理的变化 - 执行时机绑定浏览器渲染流程,而非 JS 引擎调度
- 不遵循微任务“无限递归可能阻塞页面”的行为(ResizeObserver 回调内触发的 resize 不会再次同步触发自身)
实际开发中的注意事项
理解执行时序有助于避免常见陷阱:
- 不要依赖 ResizeObserver 回调中能立刻读取最新 DOM 布局:它在 layout 后、paint 前,此时 getBoundingClientRect 等是准确的;但如果回调里修改了样式(如改 width),不会触发新一轮 layout,需等下一帧
-
避免在回调中执行耗时操作:它会延迟渲染,造成卡顿;复杂逻辑建议用
requestIdleCallback或拆分处理 - MutationObserver 和 ResizeObserver 的顺序有差异:DOM 变更通常先触发 MutationObserver,再触发 ResizeObserver(因为尺寸变化常是 DOM 变更的结果),但不能绝对依赖该顺序
-
主动触发 resize 无法绕过队列机制:即使手动调用
element.style.width = '200px',ResizeObserver 也不会立即回调,仍要等到下一次渲染检查周期
简单验证执行顺序的方法
可在控制台运行以下代码观察日志顺序:
const ro = new ResizeObserver(() => console.log('ResizeObserver'));
ro.observe(document.body);
Promise.resolve().then(() => console.log('Promise microtask'));
queueMicrotask(() => console.log('queueMicrotask'));
setTimeout(() => console.log('setTimeout'), 0);
// 输出顺序通常是:
// Promise microtask
// queueMicrotask
// ResizeObserver
// setTimeout
这印证了 ResizeObserver 回调位于微任务末尾、宏任务开头的位置。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










