Angular:是否应该取消订阅由 shareReplay(1) 传输的 forkJoin?

Angular : Should unsubscribe from forkJoin piped by shareReplay(1)?

我想知道是否有必要取消订阅 来自 forkJoin([..]) 使用一组由 shareReplay(..) 操作员传输的 HttpClient Observables 制作的 Observables?

我读到 forkJoin(..) 它会自动取消订阅,但我不知道在使用 shareReplay(..) 运算符进行管道传输时是否也是这种情况,因为我读到这个可以导致 内存泄漏如果使用不当会出现问题。

我的以下示例用于缓存 Http 请求结果,这是我的代码块:

服务:

@Injectable()
export class HttpcatcherService {
  codeNafsObservable$: Observable<any>;
  constructor(private positionservice: PositionserviceService) {}

  getAllEntities(refresh?: boolean): Observable<any> {
    if (!this.codeNafsObservable$ || refresh) {
      this.codeNafsObservable$ = forkJoin([
        this.positionservice.getPosts(), // returns Observable
        this.positionservice.getComments(), // returns Observable
      ]).pipe(
        map((result) => {
          return {
            secondNaf: result[0],
            thirdNaf: result[1],
          };
        }),
        shareReplay(1)
      );
    }
    return this.codeNafsObservable$;
  }
}

我的组件(知道我在不同的组件中也使用相同的 way/logic):

@Component({
  selector: 'hello',
  templateUrl: './hello.component.html',
  styles: [`h1 { font-family: Lato; }`],
})
export class HelloComponent implements OnInit {
  constructor(private httpCat: HttpcatcherService) {}
  ngOnInit(): void {
    this.httpCat
    .getAllEntities()
    .subscribe(
     (result) => {
      console.log(result);
    });
  }
}

我认为 forkJoinshareReplay 一起使用不会导致任何内存泄漏问题。原因如下:

  • forkJoin 当所有 Observables 作为参数传入时通知 complete。在这种情况下,由于传递给 forkJoin 的 Observable 与 http 调用有关,因此它们会在 http 调用 return 之后完成。因此,只要两个 http 调用 return,forkJoin 创建的 Observable 就会通知并完成。
  • shareReplay的作用是创建一个内部缓存,确保任何订阅都将获得上游通知的最后一个值。在这种情况下,upstream 通知的最后一个值是 forkJoin
  • 创建的 Observable 通知的唯一值

当源 Observable 是无限流时(例如使用 interval 创建的流),内存泄漏的风险或者至少是使用 shareRepley 不受控制地执行代码的风险.在这种情况下,shareReplay 创建一个永远不会完成的 Observable,即使没有订阅者,除非 ShareReplayConfigrefCount 属性 设置为 true

详细解释了此行为 in the documentation

在任何情况下,这都不是您的风险 运行,因为您拥有的源 Observable 只会发出一次然后完成。

从 RxJS 7 开始,shareReplay(1) 是一个包含以下参数的 share 包装器:

  share({
    connector: () => new ReplaySubject(1),
    resetOnComplete: false,
    resetOnError: false,
    resetOnRefCountZero: false
  })

如果您想更好地控制 shareReplay 的工作方式,请改用 share 并根据需要设置配置选项。

例如,如果您将 resetOnRefCountZero 设置为 true,那么当不再有任何订阅者时,内部 ReplaySubject 将被取消订阅。