我是否需要在ngOnDestroy内完成complete()takeUntil主题? [英] Do I need to `complete()` takeUntil Subject inside ngOnDestroy?

查看:107
本文介绍了我是否需要在ngOnDestroy内完成complete()takeUntil主题?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了避免组件内部的可观察内存泄漏,在订阅Observable之前,我正在使用takeUntil()运算符.

To avoid Observable memory leaks inside Components, I am using takeUntil() operator before subscribing to Observable.

我在组件内部编写了这样的内容:

I write something like this inside my components:

private unsubscribe$ = new Subject();

ngOnInit(): void {
  this.http
    .get('test')
    .pipe(takeUntil(this.unsubscribe$))
    .subscribe((x) => console.log(x));
}

ngOnDestroy(): void {
  this.unsubscribe$.next();
  this.unsubscribe$.complete(); // <--- ??
}

最后我的问题是:

我需要写this.unsubscribe$.complete();还是next()足够?

Do I need to write this.unsubscribe$.complete(); or next() is enough?

unsubscribe$是否会被垃圾收集器抓住而没有完成?

Is unsubscribe$ going to be grabbed by garbage collector without completing?

请说明是否存在差异或无关紧要.我不希望我的组件出现内存泄漏.

Please explain is there difference or doesn't matter. I don't want memory leaks in my components.

推荐答案

简短的回答,不需要,但这也不会造成伤害.

Short answer, no this is not needed, but it also doesn't hurt.

长答案:

仅当它阻止垃圾收集时才需要取消订阅/按角度完成,因为订阅涉及某些主题,由于该主题的存在,该主题将比要收集的组件寿命更长.这就是造成内存泄漏的方式.

Unsubscribing / completing in angular is only needed when it prevents garbage collection because the subscription involves some subject that will outlive the component due to be collected. This is how a memory leak is created.

如果我有服务,

export class MyService {
  mySubject = new Subject();
}

(仅在根中提供)(这是一个单例,在实例化后将不会被销毁),以及一个注入此服务并订阅其主题的组件

which is provided in root and only root (which means it's a singleton and will never be destroyed after instantiation) and a component that injects this service and subscribes to it's subject

export class MyLeakyComponent {
  constructor(private myService: MyService) {
    this.myService.mySubject.subscribe(v => console.log(v));
  }
}

这正在造成内存泄漏.为什么?因为MyLeakyComponent中的预订是由MyService中的主题引用的,所以只要MyService存在并保留对它的引用,就无法进行MyLeakyComponent的垃圾回收,并且MyService在应用程序的生命周期中将一直存在.每当您实例化MyLeakyComponent时,这都会变得复杂.要解决此问题,您必须取消订阅或在组件中添加终止运算符.

this is creating a memory leak. Why? because the subscription in MyLeakyComponent is referenced by the subject in MyService, so MyLeakyComponent can't be garbage collected so long as MyService exists and holds a reference to it, and MyService will exist for the life of the application. This compounds everytime you instantiate MyLeakyComponent. To fix this, you must either unsubscribe or add a terminating operator in the component.

但是此组件:

export class MySafeComponent {
  private mySubect = new Subject();
  constructor() {
    this.mySubject.subscribe(v => console.log(v));
  }
}

是完全安全的,将被无问题地垃圾收集.没有外部持久实体拥有对此的引用.这也是安全的:

is completely safe and will be garbage collected without issue. No external persisting entity holds a reference to it. This is also safe:

@Component({
  providers: [MyService]
})
export class MyNotLeakyComponent {
  constructor(private myService: MyService) {
    this.myService.mySubject.subscribe(v => console.log(v));
  }
}

现在注入的服务由组件提供,因此该服务和该组件将一起被销毁,并且可以安全地收集垃圾,因为外部引用也将被销毁.

now the inejected service is provided by the component, so the service and the component will be destroyed together and can be safely garbage collected as the external reference will be destroyed as well.

这也是安全的:

export class MyHttpService { // root provided
  constructor(private http: HttpClient) {}

  makeHttpCall() {
    return this.http.get('google.com');
  }
}

export class MyHttpComponent {
  constructor(private myhttpService: MyHttpService) {
    this.myhttpService.makeHttpCall().subscribe(v => console.log(v));
  }
}

因为http调用是一类可终止的可观察变量,所以它们在调用完成后自然终止,因此无需手动完成或取消订阅,因为外部引用自然完成后就消失了.

because http calls are a class of observables that are self terminating, so they terminate naturally after the call completes, so no need to manually complete or unsubscribe, as the external reference is gone once it naturally completes.

关于您的示例: unsubscribe$主题位于组件的本地,因此它不可能导致内存泄漏.任何本地学科都是如此.

As to your example: the unsubscribe$ subject is local to the component, thus it cannot possibly cause a memory leak. This is true of any local subject.

关于最佳做法的说明: 可观察到的是复杂的.看起来完全安全的一个对象可能以微妙的方式涉及到外部主题.为了完全安全/如果您对可观察物不太满意,通常建议您取消订阅所有非终止性可观察物.除了您自己花费的时间之外,没有其他缺点.我个人发现unsubscribe $信号方法很黑,认为它污染/混淆了您的流.对我来说最简单的是这样的:

A note on best practices: Observables are COMPLEX. One that might look completely safe, could involve an external subject in a subtle manner. To be totally safe / if you're not extremely comfortable with observables, it is generally recommended that you unsubscribe from all non terminating observables. There isn't a downside other than your own time spent doing it. I personally find the unsubscribe$ signal method hacky and think it pollutes / confuses your streams. the easiest to me is something like this:

export class MyCleanedComponent implements OnDestroy {
  private subs: Subscription[] = [];
  constructor(private myService: MyService) {
    this.subs.push(
      this.myService.mySubject.subscribe(v => console.log(v)),
      this.myService.mySubject1.subscribe(v => console.log(v)),
      this.myService.mySubject2.subscribe(v => console.log(v))
    );
  }

  ngOnDestroy() {
    this.subs.forEach(s => s.unsubscribe());
  }
}

但是,防止泄漏的最佳方法是尽可能使用angular提供的异步管道.它为您处理所有订阅管理.

However, the single BEST method for preventing leaks is using the async pipe provided by angular as much as possible. It handles all subscription management for you.

这篇关于我是否需要在ngOnDestroy内完成complete()takeUntil主题?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆