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

如何利用requestAnimationFrame终止类中的递归循环并避免内存泄漏?

如何终止基于requestAnimationFrame的递归循环并避免内存泄漏

咱们先来拆解下你遇到的问题:

  1. 你用bind传递isLoopOnFlg的方式没用——因为bind会把调用那一刻的变量值固定下来,后续修改isLoopOnFlg并不会更新已经绑定的参数,所以_loop里永远拿到的是初始的true。
  2. 没有取消requestAnimationFrame的回调,导致即使你想终止,已经调度的回调还会继续执行,甚至持续递归,造成内存泄漏。

下面是调整后的完整代码,我会逐段解释关键改进点:

class Foo {
  private isLoopOnFlg: boolean = false;
  private animationRequestId: number | null = null; // 新增:保存动画请求ID,用于取消回调

  public StartLoop() {
    // 防重复启动:如果已经在循环中,先停止再重启
    if (this.isLoopOnFlg) {
      this.StopLoop();
    }
    this.isLoopOnFlg = true;
    this.recursiveLoopWithDelay(() => {
      // 这里写你的DOM操作逻辑
      console.log('循环执行中...');
    }, 1000);
  }

  public StopLoop() {
    this.isLoopOnFlg = false;
    // 取消未执行的动画请求,彻底终止循环并避免内存泄漏
    if (this.animationRequestId !== null) {
      window.cancelAnimationFrame(this.animationRequestId);
      this.animationRequestId = null;
    }
  }

  private recursiveLoopWithDelay(loopFn: any, delay: number) {
    let stamp = Date.now();

    // 用箭头函数捕获类实例的this,确保能访问到最新的isLoopOnFlg
    const _loop = () => {
      // 先检查循环是否被终止,是则直接返回,不再递归
      if (!this.isLoopOnFlg) {
        this.animationRequestId = null;
        return;
      }

      // 原有的延迟执行逻辑
      if (Date.now() - stamp >= delay) {
        loopFn();
        stamp = Date.now();
      }

      // 保存当前的动画请求ID,方便StopLoop时取消
      this.animationRequestId = window.requestAnimationFrame(_loop);
    };

    // 启动第一个循环
    this.animationRequestId = window.requestAnimationFrame(_loop);
  }
}

关键改进点说明:

  • 实时访问isLoopOnFlg:用箭头函数定义_loop,它会自动捕获外部的this(也就是Foo类的实例),这样每次执行_loop时,访问的都是最新的isLoopOnFlg状态,而不是固定的初始值。
  • 取消动画请求:新增animationRequestId变量保存每次requestAnimationFrame的返回ID,在StopLoop中调用cancelAnimationFrame取消还没执行的回调,彻底切断递归链,避免内存泄漏。
  • 防重复启动:在StartLoop里先检查是否已经在循环中,避免多次调用导致多个循环同时运行。

为什么你的原方案无效?

你之前用_loop.bind(this, this.isLoopOnFlg)传递参数,本质是把调用bind时的isLoopOnFlg值(也就是true)复制了一份作为参数传给_loop,后续修改isLoopOnFlg并不会改变这个已经绑定的参数值,所以_loop永远认为循环应该继续,自然无法终止。

这样调整后,调用StopLoop()就能立即终止递归循环,同时也不会留下内存泄漏的隐患啦!

内容的提问来源于stack exchange,提问作者young-ceo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 12:47:36