angular - 我需要 `complete()` takeUntil ngOnDestroy 内的主题吗?

标签 angular typescript rxjs rxjs6

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

我在我的组件中写了这样的东西:

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()足够?

unsubscribe$没有完成就会被垃圾收集器抓取?

请解释是否有区别或无关紧要。我不希望我的组件中出现内存泄漏。

最佳答案

简短的回答,不,这不是必需的,但也没有伤害。

长答案:

仅当它阻止垃圾收集时才需要取消订阅/完成 Angular,因为订阅涉及一些主题,这些主题将超过要收集的组件。这就是内存泄漏的产生方式。

如果我有服务:

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

它在 root 中提供并且仅在 root 中提供(这意味着它是一个单例,并且在实例化后永远不会被销毁)和一个注入(inject)此服务并订阅它的主题的组件
export class MyLeakyComponent {
  constructor(private myService: MyService) {
    this.myService.mySubject.subscribe(v => console.log(v));
  }
}

这是造成内存泄漏。为什么?因为MyLeakyComponent中的订阅是被MyService中的subject引用的,所以MyLeakyComponent只要MyService存在并且持有对它的引用就不能被垃圾回收,并且MyService会在应用的生命周期中一直存在。每次实例化 MyLeakyComponent 时都会复合。要解决此问题,您必须取消订阅或在组件中添加终止运算符。

但是这个组件:
export class MySafeComponent {
  private mySubect = new Subject();
  constructor() {
    this.mySubject.subscribe(v => console.log(v));
  }
}

是完全安全的,并且会毫无问题地被垃圾收集。没有外部持久实体持有对它的引用。这也是安全的:
@Component({
  providers: [MyService]
})
export class MyNotLeakyComponent {
  constructor(private myService: MyService) {
    this.myService.mySubject.subscribe(v => console.log(v));
  }
}

现在注入(inject)的服务由组件提供,因此服务和组件将一起销毁,并且可以安全地进行垃圾回收,因为外部引用也将被销毁。

这也是安全的:
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 调用是一类可自终止的 observable,所以它们会在调用完成后自然终止,因此无需手动完成或取消订阅,因为外部引用一旦自然完成就消失了。

至于你的例子:unsubscribe$ subject 是组件本地的,因此它不可能导致内存泄漏。任何本地主题都是如此。

关于最佳实践的说明:
Observables 是复杂的。一个看起来完全安全的,可能以一种微妙的方式涉及一个外部主题。为了完全安全/如果您对 observables 不太满意,通常建议您取消订阅所有非终止 observables。除了您自己花时间做这件事之外,没有其他缺点。我个人觉得 unsubscribe$ 信号方法很老套,并认为它会污染/混淆你的流。对我来说最简单的是这样的:
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 提供的异步管道。它为您处理所有订阅管理。

关于angular - 我需要 `complete()` takeUntil ngOnDestroy 内的主题吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57007118/

相关文章:

angular - 是否可以将响应式(Reactive)数组控件绑定(bind)到 ngx-datatable?

angular - Angular 中 i18n 翻译文件的更新

node.js - NPM 无法安装任何范围内的包

javascript - TypeScript 类和 JS 范围混淆(再次)

AngularFire .subscribe => console.log 在使用 map 或 flatMap 后返回 undefined

如果不支持浏览器,Angular 2 如何显示警告页面

javascript - 循环直到在 Javascript 中找到一个空数组

typescript - 浏览器单元测试(Typescript + Webpack)

javascript - RxJS - 仅当其他人在延迟期间不发射时发射

Angular 6 router.events.filter 'filter' 在类型 'Observable<Event>' 上不存在