Angular Service被销毁了,但是为什么仍然可以观察到订阅?

时间:2019-12-12 12:45:09

标签: angular rxjs

使用Angular,我在组件中提供了服务。该服务具有可观察的对象,并且组件已订阅该可观察的对象。我曾期望我不需要取消订阅/可观察项,因为该服务应随组件一起被销毁,因此也应被可观察项破坏。但是,快速测试表明该可观察对象还活着。

发生什么事了? 可观察的运行是否在服务之外?还是在提供服务的组件被销毁时,服务实际上并未被销毁?

5 个答案:

答案 0 :(得分:0)

首先要注意的是,Angular Service是单例的,不会被销毁。

第二,您的可观察对象是在组件中订阅的,而不是在服务中订阅的。 最后回答您的问题,

在销毁组件时不会销毁预订。 您将必须手动退订。 有几种方法可以做到这一点,最简单的方法是

-不要订阅,而是在模板中使用异步管道。

Read this blog post in medium了解有关取消订阅该组件上所有订阅的更好方法的更多信息

答案 1 :(得分:0)

如果您订阅了一个未完成的可观察对象,则会发生内存泄漏。即使该服务是由组件提供的,您也可以在服务中创建一个可观察的对象并将其返回给组件。现在,可观察对象已被组件引用,并且与创建它的服务无关。销毁组件后,它将被标记为垃圾回收,但在垃圾回收器清理资源之前,它仍存在于内存中。您仍然需要取消订阅未完成的可观察项目。

有一些选择

  1. 使用异步管道,因为它将为您管理订阅并取消订阅
  2. 保留对订阅的引用,并在ngOnDestroy上取消订阅
  3. 对对象使用直到直到使对象发出ngOnDestroy。

takeUntil的好处是您可以使用它来管理多个订阅,而不必分别跟踪每个订阅。

1:异步管道

data$ = this.testService.testObservable();

并在视图中

<ng-container *ngIf="data$ | async as data">
  {{ data | json }}
</ng-container>

2:退订

this.subscription = this.testService.testObservable().subscribe(data => console.log(data));

ngOnDestroy() {
  if (this.subscription) {
    this.subscription.unsubscribe();
  }
}

3:takeUntil

finalise = new Subject<void>();

this.subscription = this.testService.testObservable().pipe(takeUntil(this.finalise)).subscribe(data => console.log(data));

ngOnDestroy() {
  this.finalise.next();
  this.finalise.complete();
}

答案 2 :(得分:0)

在您的情况下使用takeUntil。您需要创建/初始化一个主题,该主题将在组件销毁时完成。

private unsubscribe: Subject<void> = new Subject<void>();


this.testService.testObservable().pipe(takeUntil(this.unsubscribe))
.subscribe(data => console.log(data));

完成组件销毁

使用主题和takeUntil,您可以在ngOnDestory中一次性取消订阅多个可观察对象。

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

答案 3 :(得分:0)

当我们返回或使用间隔时,observable将连续接收流,直到我们停止它为止。为了获得最佳实践,您应该使用interval observable和RxJS运算符来自动销毁订阅。

// RxJS v6+
import { interval, timer } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

//emit value every 1s
const source = interval(1000);
//after 5 seconds, emit value
const timer$ = timer(5000);
//when timer emits after 5s, complete source
const example = source.pipe(takeUntil(timer$));
//output: 0,1,2,3
const subscribe = example.subscribe(val => console.log(val));

[注意]如果使用setInterval,则需要使用ngOnDestroy明确取消订阅

答案 4 :(得分:-1)

根据yurzui的评论,我得出以下结论:

尽管服务被销毁(因为它是在销毁的组件中提供的),但可观察和订阅仍在该服务和组件之外继续工作。我认为这在某个时候没有被收集,所以我明确地清理了它。

我的问题不是如何取消可观察对象的订阅,但是不管我认为分享我的实际解决方案来管理我希望销毁的订阅都是有用的。我在destroy()周期中父组件调用的服务上创建了ngOnDestroy函数。在此函数中,我从服务中所有无限可观察量发出完整的信息。 这使我免于重复自己的工作,也不再订阅所有子组件。

为我服务:

private subject = new BehaviorSubject<string>(null);

public testObservable(): Observable<string> {
    // ... 
    this.subject.next('result');
    return this.subject.asObservable();
}

destroy() {
    this.subject.complete();
}

在我的组件中:

ngOnInit() {
   this.testService.testObservable().subscribe(data => console.log(data));
}

ngOnDestroy() {
    this.testService.destroy();
}

编辑

我已经包括了一个有效的堆叠闪电战,以防万一我的解决方案是不确定的:https://stackblitz.com/edit/destroyservice。我喜欢的是,我通过3行代码取消了6个订阅的订阅,并且不需要在任何子组件中包括ngOnDestroy。