You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

递归调用requestAnimationFrame的作用?两种tick实现有何差异?

requestAnimationFrame在定时器代码中的作用及递归实现差异解析

一、requestAnimationFrame的作用

先看这段代码:

tick() {
  if (this.running) {
    this.remaining = Math.max(0, this.end - Date.now());
    requestAnimationFrame(() => this.tick());
  }
}

requestAnimationFrame在这里的核心作用是让tick方法的执行和浏览器的渲染节奏绑定:

  • 它会告诉浏览器:"等你下一次要重绘页面的时候,再执行我传的这个tick回调"。浏览器的重绘频率通常是60次/秒(约16.7ms每帧),所以tick会跟着这个节奏每帧执行一次,更新remaining(定时器剩余时间)。
  • 这样做的好处:如果组件要根据remaining更新UI(比如进度条),能保证UI更新和浏览器渲染步调一致,不会出现卡顿;另外,当页面切换到后台标签页时,浏览器会自动降低甚至暂停requestAnimationFrame的执行,能节省CPU和内存资源。

二、与直接递归调用的差异

直接递归的实现代码:

tick() {
    if (this.running) {
      this.remaining = Math.max(0, this.end - Date.now());
      this.tick()
    }
}

两者的核心差异体现在这几点:

  • 执行频率与页面响应:直接递归是同步调用,会在当前调用栈里无限重复执行tick,频率远高于浏览器渲染帧率,瞬间就会把CPU占满,导致页面完全卡死,根本无法响应其他操作。而requestAnimationFrame是每帧执行一次,不会阻塞页面。
  • 资源占用合理性:直接递归不管页面是否在前台,都会疯狂执行,浪费系统资源;requestAnimationFrame会适配页面状态,后台时自动降频或暂停,减少不必要的计算。
  • UI更新可见性:直接递归的同步执行会阻塞浏览器的渲染线程,哪怕remaining一直在更新,浏览器也没时间渲染UI变化,用户看不到定时器的进度。而requestAnimationFrame的回调在重绘前执行,更新后浏览器能立刻渲染新UI,用户能看到实时变化。
  • 调用栈风险:直接递归是同步递归,调用栈会不断加深,很快就会触发"栈溢出"错误;requestAnimationFrame的回调是异步执行的,每次回调都在新的调用栈里运行,不会出现栈溢出问题。

内容的提问来源于stack exchange,提问作者Ole

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 19:06:06