requestAnimationFrame(rAF)是浏览器在下一次重绘前主动调度的回调机制,不进入事件循环队列,而是与渲染管线协同,在每帧渲染前执行,确保DOM读写高效合并且匹配60fps节奏。

requestAnimationFrame(简称 rAF)不是直接插入事件循环队列的异步任务,而是由浏览器在**下一次重绘前**主动调度的回调机制——它不竞争宏任务或微任务,而是与渲染管线深度协同。
它不走事件循环,但受事件循环影响
rAF 回调不会被放进宏任务队列(如 setTimeout)或微任务队列(如 Promise.then)。浏览器会在每次「渲染帧开始前」统一收集所有已注册的 rAF 回调,并在样式计算、布局、绘制等渲染步骤之前执行它们。但它的触发时机依赖于事件循环是否空闲:如果主线程正持续执行长任务(比如一个 100ms 的同步循环),rAF 回调就会被推迟,直到该任务结束、控制权交还给浏览器,浏览器才有机会安排下一帧。
执行时机严格绑定渲染帧节奏
典型浏览器以 ~60fps 运行,即每 16.6ms 尝试渲染一帧。rAF 的回调总是在该帧的「渲染前阶段」运行,确保你能在布局/绘制发生前读取最新 DOM 状态(如 offsetTop)、修改样式(如 transform),且这些修改能被合并在单次重排重绘中处理。例如:
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
- 你在点击事件中调用 rAF → 回调不会立刻执行,而是排队等到下一个渲染帧开始前;
- 你在 rAF 回调里又调用 rAF → 新回调会排到再下一帧,形成稳定节拍;
- 页面在后台标签页时,浏览器通常会降频甚至暂停 rAF(降到 ~1fps 或暂停),节省资源。
与 setTimeout(0) 和 Promise.then 的关键区别
setTimeout(0) 是宏任务,会在当前任务结束后、下次事件循环tick中执行,时间不可控(可能远小于或大于 16ms);Promise.then 是微任务,紧接在当前同步代码后、渲染前执行,但不保证在渲染帧边界上。而 rAF 提供的是「渲染一致性」:你的逻辑一定发生在帧内,且有最大概率被浏览器优化为零延迟重绘。因此动画、滚动响应、Canvas 绘制等场景应优先用 rAF,而非靠 setTimeout 模拟帧率。
实际使用中的调度建议
- 避免在 rAF 回调中做耗时计算(如遍历大数组、复杂 layout 强制同步读取)——它会拖慢整帧,导致掉帧;
- 需要读 DOM 布局信息(如 getBoundingClientRect)时,放在 rAF 开头;需要写样式推动动画时,放在同一 rAF 中靠后位置,让浏览器有机会合并;
- 若需精确控制帧节奏(如游戏主循环),可用 performance.now() 在 rAF 内测 delta 时间,而不是依赖帧号计数;
- 取消用 cancelAnimationFrame(handle),尤其在组件卸载或动画中断时,防止内存泄漏和无效回调堆积。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










