[英]Detect when a Subject has no more subscriptions
我正在實現一個 angular 服務,讓消費者根據他們的 id 觀察各種值:
它的本質是這樣的:
private subjects = new Map<number, Subject<any>>();
public subscribe(id: number, observer: any): Subscription {
// try getting subject for this id (or undefined if it does not yet exist)
let subj = this.subjects.get(id);
// create subject if it does not yet exist
if (!subj) {
subj = new Subject<any>();
this.subjects.set(id, subj);
}
// subscribe observer
const subscription = subj.subscribe(observer);
// set up teardown logic (gets called when subscription is unsubscribed)
subscription.add(() => {
// remove subject from the map, if this was the last subscription
if (subj.observers.length == 0) {
this.subjects.delete(id);
}
});
// return subscription
return subscription;
}
以上工作正常,但 API 使用起來有點麻煩(在消費者中,我需要手動跟蹤所有訂閱並確保正確取消訂閱)。
我希望有一個返回Observable
的方法,如下所示:
public subscribe(id: number): Observable<any> {
// TODO: Return an observable for this id and make sure that
// its corresponding subject is in the map iff at least one of the observables
// for this id has at least one subscription.
return ...;
}
因為這將允許我使用async
pipe 直接從組件模板訂閱我需要的值,其中 angular 將負責取消訂閱觀察者。
但是我不太清楚如何實現邏輯以在不再使用時從Map
中刪除未使用的Subject
。 有什么好的方法嗎?
我認為你可以嘗試這樣的事情:
function subscribe(id: number): Observable<any> {
/* ... */
return sbj
.pipe(
finalize(() => {
if (subj.observers.length == 0) {
this.subjects.delete(id);
}
})
);
}
有了這個,您還可以將async pipe 與Subject.lift
返回的AnonymousSubject
一起使用(作為Subject.pipe()
的結果調用)。 AnonymousSubject
確保觀察者(例如來自模板)將被添加到“AnonymousSubject 's parent
主題”列表中。
finalize()
在源(例如Subject
)被取消訂閱時被調用。 這可能在組件被銷毀時發生,也可能在發生complete
/ error
事件時發生,這也包括Subject
完成時的情況。 當一個Subject
完成時,它會向它的所有訂閱者發送一個完整的通知,這意味着觀察者最終將被自動從Subject
的觀察者列表中刪除。
show1 = true;
show12 = true;
show2 = true;
v1$: Observable<any>;
v12$: Observable<any>;
v2$: Observable<any>;
constructor(public valueService: ValueService) {
}
async ngOnInit() {
await this.sleep(2000);
// const s11 = this.valueService.subscribe(1, v => this.v1 = v);
this.v1$ = this.valueService.subscribe(1);
await this.sleep(2000);
// const s21 = this.valueService.subscribe(2, v => this.v2 = v);
this.v2$ = this.valueService.subscribe(2);
await this.sleep(2000);
// const s12 = this.valueService.subscribe(1, () => {});
this.v12$ = this.valueService.subscribe(1);
await this.sleep(2000);
// s12.unsubscribe();
this.show12 = false
await this.sleep(2000);
// s11.unsubscribe();
this.show1 = false;
await this.sleep(2000);
// s21.unsubscribe();
this.show2 = false
}
<div *ngIf="show1">
v1: {{ v1$ | async }}
</div>
<div *ngIf="show12">
v12: {{ v12$ | async }}
</div>
<div *ngIf="show2">
v2: {{ v2$ | async }}
</div>
public subscribe(id: number): Observable<any> {
let subj = this.subjects.get(id);
if (!subj) {
subj = new Subject<any>();
this.subjects.set(id, subj);
}
return subj.pipe(
finalize(() => {
if (subj.observers.length === 1) {
this.subjects.delete(id);
}
})
)
}
正如@ggradnig 提到的,檢查應該是subj.observers.length === 1
,因為finalize()
,至少在 RxJs 6.5.x
中,在發生任何其他取消訂閱之前運行它的回調。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.