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

Angular服务取消订阅后无法重新订阅的解决方案咨询

Angular订阅取消后重启startTimer异常的解决方案

问题场景

我有一个Angular服务,包含以下属性和函数:

timerSubscription: Subscription;
pollingSubscription: Subscription;
incrementTimerSubscription: Subscription;

async startTimer(idTask: number) {
  this.timerSubscription = this.folderService
    .getTimerForTemplate(idTask)
    .pipe(
      tap(res => this.timer$.next(res.seconds)),
      switchMap(() => this.folderService.startTimerForTemplate(idTask))
    )
    .subscribe(() => {
      this.setMyState({ idTask: idTask, isTimerStarted: true });

      this.incrementTimerSubscription = interval(1000)
        .pipe(
          tap(() => {
            this.timer$.next(this.timer$.value + 1);
          })
        )
        .subscribe();

      this.pollingSubscription = interval(20000)
        .pipe(
          switchMap(() => {
            return this.folderService.incrementTimerForTemplate(idTask);
          })
        )
        .subscribe();
    });
}

incrementAndStartTimer(idTaskToIncrement: number, idTaskToStart: number, automaticSwitch?: boolean) {
  if (this.timerSubscription) {
      this.timerSubscription.unsubscribe();
    }
    if (this.pollingSubscription) {
      this.pollingSubscription.unsubscribe();
    }
    if (this.incrementTimerSubscription) {
      this.incrementTimerSubscription.unsubscribe();
    }

    if (automaticSwitch) {
      this.folderService
        .incrementTimerForTemplate(idTaskToIncrement, automaticSwitch)
        .pipe()
        .subscribe(() => {
          this.startTimer(idTaskToStart);
        });
    } else {
      this.folderService
        .incrementTimerForTemplate(idTaskToIncrement)
        .pipe()
        .subscribe(() => {
          this.startTimer(idTaskToStart);
        });
    }
}

调用startTimer时运行正常,但调用incrementAndStartTimer(该函数会取消现有订阅)后,再次调用startTimer出现异常,推测是之前的订阅取消操作导致的,需要解决这个问题。

核心问题与修复方案

1. 取消订阅后必须重置订阅对象

取消订阅后,timerSubscription等变量仍持有已取消的订阅实例,后续操作会出现状态混乱。在unsubscribe()后必须将变量置为null:

if (this.timerSubscription) {
  this.timerSubscription.unsubscribe();
  this.timerSubscription = null;
}
if (this.pollingSubscription) {
  this.pollingSubscription.unsubscribe();
  this.pollingSubscription = null;
}
if (this.incrementTimerSubscription) {
  this.incrementTimerSubscription.unsubscribe();
  this.incrementTimerSubscription = null;
}

2. 用统一容器管理所有订阅避免嵌套冲突

原代码中嵌套订阅、分散管理订阅的方式容易引发内存泄漏和状态异常,改用Subscription容器统一管理所有子订阅:

  • 先在服务中声明订阅容器:
private subscriptions = new Subscription();
  • 修改startTimer,将所有订阅添加到容器:
async startTimer(idTask: number) {
  // 清空并重置订阅容器
  this.subscriptions.unsubscribe();
  this.subscriptions = new Subscription();

  const timerSub = this.folderService
    .getTimerForTemplate(idTask)
    .pipe(
      tap(res => this.timer$.next(res.seconds)),
      switchMap(() => this.folderService.startTimerForTemplate(idTask))
    )
    .subscribe(() => {
      this.setMyState({ idTask: idTask, isTimerStarted: true });

      const incrementSub = interval(1000)
        .pipe(
          tap(() => {
            this.timer$.next(this.timer$.value + 1);
          })
        )
        .subscribe();
      this.subscriptions.add(incrementSub);

      const pollingSub = interval(20000)
        .pipe(
          switchMap(() => this.folderService.incrementTimerForTemplate(idTask))
        )
        .subscribe();
      this.subscriptions.add(pollingSub);
    });
  this.subscriptions.add(timerSub);
}
  • 修改incrementAndStartTimer,统一通过容器取消订阅,同时用switchMap替代嵌套订阅:
incrementAndStartTimer(idTaskToIncrement: number, idTaskToStart: number, automaticSwitch?: boolean) {
  // 统一取消所有订阅
  this.subscriptions.unsubscribe();
  this.subscriptions = new Subscription();

  const incrementSub = (automaticSwitch 
    ? this.folderService.incrementTimerForTemplate(idTaskToIncrement, automaticSwitch)
    : this.folderService.incrementTimerForTemplate(idTaskToIncrement)
  ).pipe(
    switchMap(() => this.startTimer(idTaskToStart))
  ).subscribe();
  this.subscriptions.add(incrementSub);
}

3. 重置timer$状态避免旧值干扰

重启定时器前,先重置timer$的初始值,避免基于旧值累加导致异常:

// 在清空订阅容器时同步重置timer$
this.subscriptions.unsubscribe();
this.subscriptions = new Subscription();
this.timer$.next(0);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 15:49:51