如何避免 Angular 9 中复杂 RxJS 管道的内存泄漏?

net*_*djw 5 memory-leaks rxjs typescript angular

我的 Angular + RxJS 应用程序中有一点复杂的管道结构:

远程数据.service.ts:

getAll(url): Observable<any[]> {
  return this.http.get(url, this.options)
    .pipe(map((result: any[]) => result));
}
Run Code Online (Sandbox Code Playgroud)

代理.服务.ts:

// localDataService use my LocalDataService
// remoteDataService use my RemoteDataService

getAll(model): Observable<any[]> {
  if (model.use_localstorage) {
    return this.localDataService.getAll(model)
      .pipe(map((result: any[]) => result));
  } else {
    return this.remoteDataService.getAll(model.url)
      .pipe(map((result: any[]) => result));
  }
}
Run Code Online (Sandbox Code Playgroud)

助手.service.ts:

getAll(model): Observable<any[]> {
  // do some general fancy things...
  return this.proxyService.getAll(model)
      .pipe(map((result: any) => result));
  }
}
Run Code Online (Sandbox Code Playgroud)

然后在我的组件中:

export class MyComponent {
  helperService = new HelperService();
  model = new MyModel();

  getAll() {
    this.helperService.getAll().subscribe((result: any[]) => {
      // parse result
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我从远程数据服务、代理服务、帮助服务和组件构建了一个管道。当然,原因是为了将每个功能彼此分离,并使我的服务更具可重用性。

我的目标是避免内存泄漏。

问题是:如果我想将take(1)RxJS 运算符放入管道中,那么将其放入管道末尾就足够了,或者.subscribe()需要将其放入每个服务和组件之前吗?

在这种情况下避免内存泄漏的最佳实践是什么?

Lux*_*lem 7

这取决于。pipe(take(1))确保您获得“可观察到的单个值”的含义,即它将发出一个值,然后完成并关闭订阅,因此不会出现内存泄漏。如果您的服务或函数本身仅发出单个值,pipe(take(1))则不会执行任何操作。

例如,如果您有 REST 调用,则httpclient.get(...)可能会有延迟。然后,您应该使用pipe(timeout(3000))pipe(takeUntil(...))来确保订阅不存在,如果您的组件或任何内容被破坏,您不会出现内存泄漏或订阅内逻辑的意外行为。即使pipe(take(1))内存泄漏也可能存在,因为它只有在恰好发出一个值或错误后才会完成。

因此,如果您有一个http.get()并且它有网络延迟。而且您将使用“pipe(take(1))”,它仍然会导致内存泄漏,因为它只等待一个值,并且当该值到达时订阅将被触发,即使您进行调用的组件是已销毁或您已导航到应用程序的其他视图。

takeUntil(...) 对于组件很有用,如果它们被销毁,您可以触发ngDestroy().

  public isActive = new Subject();

  public ngOnDestroy(): void {
    this.isActive.next(false);
  }

  public fun():void{
    this.fooService.getValue()
                     .pipe(takeUntil(this.isActive))
                     .subscribe( value => console.log(value));
  }
Run Code Online (Sandbox Code Playgroud)

如果 Observable/Subject 完成,则在发出最后一个值后不应出现内存泄漏。

您需要处理这些情况,如果您不确定是否存在“complete()”,或者它是否只发出一个值,但延迟可能会出现问题。

顺便说一句,内存泄漏是次要问题,而且没有那么大。更成问题的是,当您不再想要时,您的订阅逻辑可能会被触发。有时,即使用户已经离开视图,您也希望触发订阅中的逻辑(例如告诉用户某件事成功的通知)。所以这取决于。

此外,您还可以将所有订阅存储在单个订阅中,如果您想处置它们,则可以取消订阅。

  public subs = new Subscription();

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

  public fun():void{
    const bar: Subscription = this.fooService.getValue()
                     .subscribe( value => console.log(value));
    this.subs.add(bar);
  }
Run Code Online (Sandbox Code Playgroud)