rAF既不是微任务也不是典型宏任务,而是在微任务清空后、渲染前执行的特殊渲染相关任务;其时机确保样式更新后可立即测量,且与屏幕刷新率同步。

requestAnimationFrame(rAF)是宏任务,但它在事件循环中的位置很特殊:它不排在普通宏任务队列末尾,而是被浏览器安排在“微任务清空之后、渲染之前”执行。 这个时机决定了它既不是微任务,也不能像 setTimeout 那样被随意延后——它是浏览器为动画优化预留的“渲染前最后一道门”。
rAF 的执行时机比所有微任务都靠后
一个宏任务(比如点击事件或 script 执行)结束后,浏览器会:
- 先执行完全部微任务(Promise.then、queueMicrotask、MutationObserver)
- 再执行已注册的 rAF 回调
- 然后才进行样式计算、布局、绘制等渲染步骤
这意味着:你在 Promise.then 里修改了 DOM 样式,rAF 回调里读取 offsetHeight 或 getBoundingClientRect,拿到的就是更新后的值;但如果你在微任务里连续改十次样式,rAF 只会看到最终结果,中间变化不会触发多次布局。
rAF 不是微任务,不能替代 Promise.then
有人误以为 rAF 优先级高,就用它代替微任务做状态同步,这是错的:
立即学习“Java免费学习笔记(深入)”;
- rAF 每帧最多执行一次,且受屏幕刷新率限制(通常 60fps,即约 16ms 一帧),不适合做逻辑链式响应
- Promise.then 是纯 JS 逻辑,无渲染依赖,适合数据流转、错误捕获、链式处理
- 两者定位不同:微任务保障“JS 状态尽快一致”,rAF 保障“视觉更新与屏幕同步”
需要协调时,按目的选类型
常见组合场景和写法建议:
- 想“改完样式立刻测量” → 把测量逻辑放进 rAF 回调,避开微任务和渲染时机错位
- 想“确保 DOM 更新完成后再发请求” → 用 MutationObserver(微任务),而不是等 rAF
- 动画中需响应数据变化 → 先在微任务里更新状态,再在 rAF 里驱动样式,避免状态和视图脱节
- 滚动中频繁触发 → 用 rAF 节流,但状态聚合、防抖逻辑仍放在微任务或普通函数里
rAF 和 setTimeout(0) 的实际差异
虽然两者都是异步,但行为完全不同:
- setTimeout(0) 是宏任务,要等当前宏任务 + 所有微任务 + 上一帧渲染完成后,才排队等下一轮宏任务
- rAF 是浏览器调度的宏任务,但被强制插入到“本轮渲染前”,所以它比 setTimeout(0) 快得多,也更稳定
- 实测中,连续触发 rAF 通常在 1–2ms 内进入回调;setTimeout(0) 延迟常达 4–10ms,且受任务队列长度影响


















