标准答案
- requestAnimationFrame 会在浏览器下一次重绘前执行回调,节奏通常和屏幕刷新率对齐。它适合动画、滚动位置同步、读取布局后更新视觉状态等与渲染帧相关的任务。
- setTimeout 是指定延迟后把回调放入宏任务队列。它适合普通延时、超时兜底、轮询间隔等逻辑,但时间不保证精确,可能受到主线程阻塞、嵌套定时器、页面后台状态影响。
- requestIdleCallback 会在浏览器空闲时执行回调,适合低优先级任务,例如日志处理、非关键预计算、缓存预热。它不能用于用户可感知的关键交互,因为空闲时间不稳定。
- 选择方式:动画和视觉更新优先 rAF;明确延时或超时用 setTimeout;不急的后台工作可考虑 requestIdleCallback,并且要设置 timeout 或降级方案。
题目解析
这里关注浏览器调度。动画优先 rAF,普通延时用 setTimeout,低优先级后台任务可考虑 requestIdleCallback。
rAF 的优势是和渲染管线对齐,但它不等于性能自动变好。回调里如果频繁读写布局,仍然可能触发布局抖动,所以要注意先读后写、批量更新。
setTimeout 的 delay 是“最早多久后可以排队”,不是“多久后一定执行”。如果主线程被长任务占用,回调会被延迟。
requestIdleCallback 的空闲时间由浏览器决定,移动端和繁忙页面上可能很少触发。关键任务不能依赖它完成。
代码示例
下面例子展示动画更新、普通超时和空闲任务的典型写法。
JavaScript
function move(box, x) {
requestAnimationFrame(() => {
box.style.transform = 'translateX(' + x + 'px)'
})
}
const timer = setTimeout(() => {
console.log('timeout fallback')
}, 3000)
requestIdleCallback?.((deadline) => {
while (deadline.timeRemaining() > 0 && queue.length) {
process(queue.shift())
}
}, { timeout: 2000 })常见误区
- 用 setTimeout 固定 16ms 做动画,忽略刷新率和页面隐藏状态。
- 把 requestIdleCallback 用在用户可感知的关键交互上。
- 认为 setTimeout 的延迟时间一定精确。