繁体   English   中英

使用Angular中的RxJ,我是否只需要完成在组件中创建的Observable?

[英]With RxJs in Angular do I need to only complete the Observables created in the component?

基本上,我还是不太确定Angular组件中的反应式代码是否清理。 以下规则正确且充分吗?

我的反应式组件规则:

  • 在手册x.subscribe()上
    • 如果使用x.takeUntil(...)或类似的构造,则无需手动清理
    • 如果在组件范围内调用了x.complete() ,则无需进行手动清理
    • 其他手动.unsubscribe()需求或ngOnDestroy()
  • 在模板的可观察| 异步
    • 无需手动清理,并且兼容onPush
  • 关于手动直接创建Observable,如new Subject()
    • ngOnDestroy ()中的manual .complete()否则永远不会关闭
  • 在使用其他Observables(如Observable.merge(...))进行手动创建时:
    • 只要清理订阅,就无需手动清理

我不确定最后一点。

这是我根据这些规则重构的真实组件的完整示例,有人可以看到内存泄漏是否存在危险吗? 所有公共的$ observable都是通过模板中的异步管道进行订阅的,因此我确定订阅已得到处理。 使用new创建的主题将被手动清理,因此应该可以。 我担心的部分是Observable.combineLatest(...)可观察对象。

import { ChangeDetectionStrategy, Component, Input, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';
import { DeliveryPointId } from 'app/bso';
import { BookmarkService } from 'app/general';
import { EAccess, EPermission, ReduxGetters, ReduxService } from 'app/redux';
import * as routing from 'app/routing';
import { BehaviorSubject, Observable } from 'app/rx';

@Component({
  selector: 'app-last-opened-workitems-widget',
  templateUrl: './last-opened-workitems-widget.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LastOpenedWorkitemsWidgetComponent implements OnDestroy {

  private readonly showPartners$ = new BehaviorSubject(true);
  private readonly showServices$ = new BehaviorSubject(true);
  private readonly showTens$ = new BehaviorSubject(true);
  private readonly showWorklist$ = new BehaviorSubject(true);

  @Input() set showItems(val: boolean) { this.showWorklist$.next(!!val); }
  @Input() set showPartners(val: boolean) { this.showPartners$.next(!!val); }
  @Input() set showProcesses(val: boolean) { this.showServices$.next(!!val); }
  @Input() set showTens(val: boolean) { this.showTens$.next(!!val); }

  constructor(
    private readonly redux: ReduxService,
    private readonly bookmarks: BookmarkService,
    private readonly router: Router,
  ) { }

  canPartners$ = this.redux.watch(ReduxGetters.userAccess).map(access => access[EPermission.Partner] >= EAccess.Read);
  canServices$ = this.redux.watch(ReduxGetters.userAccess).map(access => access[EPermission.Services] >= EAccess.Read);
  canTens$ = this.redux.watch(ReduxGetters.userAccess).map(access => access[EPermission.Tens] >= EAccess.Read);
  canWorklist$ = this.redux.watch(ReduxGetters.userAccess).map(access => access[EPermission.Worklist] >= EAccess.Read);

  lastItems$ = this.bookmarks.onLastOpenedWorkitems.map(ii => [...ii].reverse());
  lastPartners$ = this.bookmarks.onLastOpenedPartners.map(ii => [...ii].reverse());
  lastProcesses$ = this.bookmarks.onLastOpenedProcesses.map(ii => [...ii].reverse());
  lastTens$ = this.bookmarks.onLastOpenedTens.map(ii => [...ii].reverse());

  hasContentPartners$ = Observable
    .combineLatest(
      this.showPartners$.distinctUntilChanged(),
      this.canPartners$,
      this.lastPartners$.map(ii => ii.length > 0))
    .map(oks => oks.every(ii => ii));

  hasContentServices$ = Observable
    .combineLatest(
      this.showServices$.distinctUntilChanged(),
      this.canServices$,
      this.lastProcesses$.map(ii => ii.length > 0))
    .map(oks => oks.every(ii => ii));

  hasContentTens$ = Observable
    .combineLatest(
      this.showTens$.distinctUntilChanged(),
      this.canTens$,
      this.lastTens$.map(ii => ii.length > 0))
    .map(oks => oks.every(ii => ii));

  hasContentWorklist$ = Observable
    .combineLatest(
      this.showWorklist$.distinctUntilChanged(),
      this.canWorklist$,
      this.lastItems$.map(ii => ii.length > 0))
    .map(oks => oks.every(ii => ii));

  hasContent$ = Observable
    .combineLatest(this.hasContentPartners$, this.hasContentServices$, this.hasContentTens$, this.hasContentWorklist$)
    .map(oks => oks.some(ii => ii));

  ngOnDestroy() {
    [this.showPartners$, this.showServices$, this.showTens$, this.showWorklist$].forEach(ii => ii.complete());
  }

  gotoPartner = (id: string) => routing.gotoPartnerItem(this.router, id);
  gotoProcess = (id: number) => routing.gotoProcess(this.router, id);
  gotoTensItem = (id: DeliveryPointId) => routing.gotoTensItem(this.router, id);
  gotoWorkitem = (id: number) => routing.gotoWorkitem(this.router, id);

}

您需要取消订阅每个订阅,例如在ngOnDestroy()方法中。

或者您可以使用诸如takeUntil()take()first()等运算符来“限制”可观察对象。

上面的规则也适用于combineLatest

不必担心AsyncPipe创建的订阅,因为AsyncPipe本身负责取消订阅其自己的订阅。

RxJS负责人Ben Lesh的文章对此主题有很好的阅读。

好的,我实际上只是使用.finally(...)加上所有中间Observable的日志记录对其进行了测试,并且在销毁组件时可以正确触发它。

因此,我宣布上述规则正确无误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM