[英]With RxJs in Angular do I need to only complete the Observables created in the component?
Basically I'm still not really sure about the cleanup of reactive code in Angular components. 基本上,我还是不太确定Angular组件中的反应式代码是否清理。 Are the following rules correct and sufficient?
以下规则正确且充分吗?
My rules for a reactive component: 我的反应式组件规则:
I'm not really sure about the last bullet point. 我不确定最后一点。
Here is a full example of a real component I refactored according to those rules, can someone see whether there is danger for a memory leak? 这是我根据这些规则重构的真实组件的完整示例,有人可以看到内存泄漏是否存在危险吗? All public $ observables are subscribed via async pipe in the template so I'm sure that the subscriptions are taken care of.
所有公共的$ observable都是通过模板中的异步管道进行订阅的,因此我确定订阅已得到处理。 The subjects created with new are manually cleaned up so that should be OK.
使用new创建的主题将被手动清理,因此应该可以。 The parts I'm worried about are the Observable.combineLatest(...) observables.
我担心的部分是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);
}
You need to unsubscribe each and every of your subscriptions, for example in the ngOnDestroy()
method. 您需要取消订阅每个订阅,例如在
ngOnDestroy()
方法中。
Or you may 'limit' the observables using operators such as takeUntil()
, take()
, first()
etc. 或者您可以使用诸如
takeUntil()
, take()
, first()
等运算符来“限制”可观察对象。
The above rules are valid for combineLatest
as well. 上面的规则也适用于
combineLatest
。
Do not worry about subscriptions created by AsyncPipe
because AsyncPipe
itself is responsible for unsubscribting for its own subscriptions. 不必担心
AsyncPipe
创建的订阅,因为AsyncPipe
本身负责取消订阅其自己的订阅。
A good read on this topic is the article by Ben Lesh who is the RxJS lead. RxJS负责人Ben Lesh的文章对此主题有很好的阅读。
OK I actually just tested it with .finally(...) plus logging for all intermediate Observables and it gets fired properly when the component is destroyed. 好的,我实际上只是使用.finally(...)加上所有中间Observable的日志记录对其进行了测试,并且在销毁组件时可以正确触发它。
Thus I declare the rules above correct und sufficient. 因此,我宣布上述规则正确无误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.