requestAnimationFrame(rAF)不是宏任务或微任务,而是浏览器在下一次重绘前、样式计算和布局之后主动调用的渲染管线钩子;它独立于事件循环,按屏幕刷新率批量执行,且同一帧内多次调用仅触发一次回调。

requestAnimationFrame(简称 rAF)不是在事件循环的某个“宏任务”或“微任务”队列里排队执行的,而是由浏览器**在下一次重绘前、紧挨着样式计算和布局之后**主动调用的回调。它的执行时机与屏幕刷新率强绑定,本质是浏览器渲染管线的一个钩子。
它不走常规事件循环队列
rAF 回调不会被放进 macroTask(如 setTimeout、setInterval)或 microTask(如 Promise.then、MutationObserver)队列。浏览器内部维护一个独立的rAF 回调队列,只在满足“即将开始一帧渲染”的前提下批量执行:
- 当页面处于活动标签页且未被冻结时,浏览器按屏幕刷新率(通常是 60Hz,即每 ~16.7ms 一帧)规划渲染帧
- 在每一帧的“渲染阶段”开始前,浏览器会清空当前收集到的所有 rAF 回调(注意:不是“注册时立即入队”,而是“在下一帧前统一执行”)
- 即使连续调用 10 次 requestAnimationFrame,也只会在**下一个渲染帧中执行一次**(除非上一帧还没结束就又触发了新帧)
它和 repaint/reflow 的关系很近
rAF 回调执行时,DOM 可能刚被修改,但此时样式已更新、布局可能已完成、绘制尚未开始。这是操作 DOM 并希望“刚好赶上下一帧绘制”的黄金时机:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 适合做动画:读取 offsetTop 等布局信息(此时 layout 已完成),再设置 transform / opacity(触发合成,不触发重排重绘)
- 不适合做耗时计算:rAF 回调必须在 16ms 内完成,否则会掉帧;长时间运行会阻塞绘制,导致卡顿
- 如果在 rAF 中又调用了 rAF,新回调会排到,形成稳定节拍
它和 setTimeout(0) / Promise.then 的对比很说明问题
写一段代码就能看清差异:
立即学习“Java免费学习笔记(深入)”;
console.log('1');setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
requestAnimationFrame(() => console.log('4'));
console.log('5');
典型输出顺序是:1 → 5 → 3 → 4 → 2(Chrome 最新版行为):
- 1 和 5 是同步代码,最先执行
- Promise.then 是 microTask,在本轮同步任务结束后立即执行 → 3
- rAF 回调在下一个渲染帧开始前执行 → 4(比 setTimeout(0) 更早,因为后者是 macroTask,要等完整一轮事件循环)
- setTimeout(0) 要等到当前帧渲染完成、事件循环再次轮转才执行 → 2(实际常在第 2 帧甚至更晚)
实际开发中的关键细节
- 页面切到后台时,大多数浏览器会暂停 rAF(降到约 1fps 或完全停止),节省资源;切回前台自动恢复
- 不能靠 rAF 实现精确计时(受帧率、系统负载、页面可见性影响),仅用于“与屏幕刷新同步”的视觉更新
- 想取消?保存 rAF 返回的 ID,用 cancelAnimationFrame(ID) —— 类似 clearTimeout
- 在非主线程(Web Worker)中无法使用 rAF,因为它依赖主线程的渲染上下文

















