Angular/RxJs - 在组件取消订阅后调用服务中 Observable 的 next() 导致 ObjectUnsubscribedError

Angular/RxJs - Calling next() on Observable in service after component was unsubscribe() cause ObjectUnsubscribedError

我有一个定义 public Subject 的 Angular 服务。稍后在某些情况下,该服务将在此主题上调用 .next()

此外,我有一个 Angular 组件,在它的 ngOnInit() 方法中 .subscribe() 到这个主题,并在它的 ngOnDestroy() 方法中调用 .unsubscribe() .

虽然服务和组件都处于活动状态,但我的代码工作正常。但是,用户可以单击我的应用程序中的其他选项卡。这导致我的组件卸载并调用其 ngOnDestroy() 方法,这意味着 Observable 已取消订阅。

从这一点开始,该组件不再存在,但该服务仍然存在 - 其他组件仍在使用它。当服务在组件调用 .unsubscribe() 之后调用 .next() 方法时,将抛出此错误 ObjectUnsubscribedErrorERROR Error: Uncaught (in promise): ObjectUnsubscribedError: object unsubscribed

此外,如果用户稍后返回到包含该组件的选项卡,则会执行 ngOnInit() 并且重新订阅此可观察对象会再次引发相同的错误。

我做错了什么,我该如何解决这些错误?

import { Subject } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class StatusService {
  public statusChange$: Subject<number>;

  public constructor(...) {
    this.statusChange$ = new Subject<number>();
  }

  public someInternalLogic(someNumber: number): void {
    this.statusChange$.next(someNumber);
    // this will throw an error if StatusComponent call .unsubscribe()
  }
}

@Component({
  selector: 'app-status',
  templateUrl: './status.component.html',
  styleUrls: ['./status.component.scss']
})
export class StatusComponent implements OnInit, OnDestroy {
  public constructor(
    private _statusService: StatusService) {}

    public ngOnInit(): void {
      this._statusService.statusChange$.subscribe(value => this._onStatusChange(value));
  }

  public ngOnDestroy(): void {
    this._statusService.statusChange$.unsubscribe();
    // after this point if the StatusService calling .next() an error will be thrown
  }

  private _onStatusChange(value: number): void {
    // ....
  }
}

您不需要取消订阅组件中的服务主题。而是在您的组件中为服务主题创建一个订阅变量,并在组件 destroy

上取消订阅
subscription!: Subscription;

public ngOnInit(): void {
    this.subscription = this._statusService.statusChange$.subscribe(value => this._onStatusChange(value));
}

public ngOnDestroy(): void {
    this.subscription.unsubscribe();
}

private _onStatusChange(value: number): void {
    // ....
}

另一种集体取消订阅 OnDestroy 事件的方法是使用 takeUntil 运算符。因此,您不必存储每个订阅,而是可以创建一个主题并在组件被销毁时触发它。

假设您的服务有多个主题,并且您的组件订阅了每个主题。在这种情况下,您必须通过 class 属性(或与此相关的一系列订阅)存储所有订阅,并且您必须手动取消订阅每个订阅。

class myComponent {

  // create any subject which allows you to push into
  // when your component gets destroyed
  private onDestroy = new Subject<void>();

  public ngOnInit(): void {
    // Subscription #1
    this._statusService.statusChange_1$.pipe(
      tap(() => /* do something */),
      // takeUntil will listen to changes of the onDestroy subject
      // and whenever you trigger it by this.onDestroy.next() it will
      // automatically unsubsribe for you
      takeUntil(this.onDestroy)
    ).subscribe();

    // Subscription #2
    this._statusService.statusChange_2$.pipe(
      takeUntil(this.onDestroy)
    ).subscribe(value => {
      /* do something else */
      // you can put your logic into your subscribe method as well
      // like you did in your question
    });

    // Any number of more subscriptions...
    // ...
  }

  public ngOndestroy(): void {
    // Notify the observers to unsibscribe
    this.onDestroy.next();
  }
}

此方法适用于任何可观察对象。