渲染并非每轮事件循环都发生,而是在宏任务结束、微任务清空后,且满足dom修改、样式变更、存在pending raf或超16.6ms任一条件时才触发;raf回调在渲染前执行,确保获取最新布局并参与本轮渲染;微任务会阻塞渲染,连续promise.then会推迟渲染;settimeout(0)是宏任务,不同步vsync,不适合帧级动画。

浏览器事件循环中,渲染不是每轮都发生,而是由浏览器在特定时机主动触发——它发生在宏任务执行完毕、微任务队列清空之后,且仅当页面有视觉更新需求时才进行。
渲染只在“合适时机”发生
浏览器不会在每次事件循环结束就强制渲染。它会检查:DOM 是否被修改、CSS 样式是否变更、是否有 pending 的 requestAnimationFrame 回调、或距离上次渲染是否已超 ~16.6ms(60Hz)。满足任一条件,才会启动渲染管线(样式计算 → 布局 → 绘制 → 合成)。
requestAnimationFrame 在渲染前执行
rAF 回调被安排在渲染阶段的最开头,也就是微任务清空后、样式计算前。这意味着:
- 你在 rAF 里读取 offsetTop 或 clientWidth,拿到的是即将用于本次渲染的最新布局结果
- 你在 rAF 里修改样式(如 transform),能确保该变更参与本轮渲染,不会掉帧
- 如果在 rAF 中又触发了 Promise.resolve().then(),那个微任务会等到本轮渲染完成后再执行
微任务会阻塞渲染
每个宏任务结束后,浏览器必须把当前所有微任务(Promise.then、MutationObserver)全部执行完,才考虑是否渲染。所以:
- 连续多个 .then 链会一直执行,直到队列为空,期间渲染被推迟
- 在 Promise 回调里改 DOM,样式变更不会立刻体现在下一帧——因为渲染还没开始
- 想让 DOM 更新“马上可见”,不能依赖微任务,而应借助 rAF 或强制触发 reflow(不推荐)
setTimeout 不等于下一帧
setTimeout(fn, 0) 是宏任务,排在下一轮事件循环开头,它和屏幕刷新不同步:
- 可能比 rAF 晚一帧甚至更多,尤其在页面忙碌时
- 无法保证与显示器垂直同步(vsync),容易导致动画撕裂或卡顿
- 适合做延迟调度,但不适合做帧级动画控制
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











