setTimeout与ResizeObserver回调执行顺序是否存在两种合法浏览器行为?
ResizeObserver回调与setTimeout执行顺序的合法性疑问
我编写了两段JavaScript代码,在Firefox中运行时,有时先输出“timeout”再输出“RO”,有时顺序相反;而同样基于Gecko引擎的Midori浏览器中,始终先输出“timeout”。
我参考资料推测ResizeObserver应该像MutationObserver一样优先执行(假设其队列微任务),但ResizeObserver的编辑草案并未明确这一点,想询问:两种输出顺序是否均为合法的浏览器行为,还是仅有一种合法?
代码示例1
const div = document.createElement("div") document.body.append(div) new ResizeObserver(() => console.log("RO")).observe(div) setTimeout(() => console.log("timeout"))
代码示例2
new ResizeObserver(() => console.log("RO")).observe(document.body) setTimeout(() => console.log("timeout"))
结论
两种输出顺序均属于合法的浏览器行为,因为ResizeObserver的回调执行时机在标准中并未被严格绑定到微任务队列,浏览器有一定的实现自由度。
详细解释
任务队列的差异
setTimeout的回调属于宏任务,会被加入到宏任务队列,等待当前执行栈清空后按顺序执行。- 虽然MutationObserver的回调明确属于微任务,会在当前宏任务结束前优先执行,但ResizeObserver的标准文档(编辑草案)并未明确将其回调的执行与微任务队列强绑定,仅规定回调会在"paint前的某个时机"触发。
浏览器实现的灵活性
- 不同浏览器(甚至同一引擎的不同浏览器)可以根据自身的渲染调度策略,选择在微任务阶段触发ResizeObserver回调,或者推迟到下一个宏任务的某个时机执行。
- Firefox的行为变化可能是因为某些场景下(比如元素尺寸变化的触发时机不同),浏览器调整了回调的调度时机;而Midori则选择了固定将其推迟到宏任务阶段,晚于
setTimeout执行。
标准的模糊性
- 当前ResizeObserver的编辑草案仅说明回调会在"resize事件触发后,下一次paint之前"执行,但未明确这一触发点属于微任务还是宏任务范畴,这就给浏览器留下了实现空间。
内容的提问来源于Stack Exchange,提问作者ByteEater
相关产品推荐
相关产品推荐

