标准答案

  1. requestAnimationFrame 会在浏览器下一次重绘前执行回调,节奏通常和屏幕刷新率对齐。它适合动画、滚动位置同步、读取布局后更新视觉状态等与渲染帧相关的任务。
  2. setTimeout 是指定延迟后把回调放入宏任务队列。它适合普通延时、超时兜底、轮询间隔等逻辑,但时间不保证精确,可能受到主线程阻塞、嵌套定时器、页面后台状态影响。
  3. requestIdleCallback 会在浏览器空闲时执行回调,适合低优先级任务,例如日志处理、非关键预计算、缓存预热。它不能用于用户可感知的关键交互,因为空闲时间不稳定。
  4. 选择方式:动画和视觉更新优先 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 的延迟时间一定精确。

作者信息