angular - 取消訂閱作為 asObservable() 返回的 ReplaySubject;
標題
如果你基本上有同樣的問題,並且你的情境是 Angular,你可能想閱讀答案中的所有評論以獲得更多背景資訊。
這個問題的簡短版本
當執行 let observe$=someReplaySubject.asObservable() 時,我們是否不再需要退訂 observe$?換句話說,我們可以從多個 Angular 元件實例中呼叫 let observe$=someReplaySubject.asObservable(),而不必擔心 notify 實例與對應的 Observable 之間的連接嗎?
假設
如果我們有一個 ReplaySubject<Todo[]> 實例(我們稱之為 rxTodo),並且我們從 Angular 組件通過以下方式訂閱它:
let todoObs$:Observable<Todo[]>=rxTodo$.asObservable() 然後即使元件被銷毀,每個元件中創建的 todoObs$ 參考也會懸掛,直到元件本身被銷毀。
背景
我正在嘗試為 Angular 實作一個 Store API,並且我有一個重放主題(replay subject)用來廣播 store 的切片變化。這裡是允許訂閱發生的方法(notifyCount 跟蹤訂閱數,所以如果沒有訂閱,我們就不進行通知):
/**
* Subscribe to receive slice updates.
* @example
<pre>
let todos$ = slice.subscribe();
</pre>
*/
public subscribe(): Observable<E[]> {
this.notifyCount++;
return this.notify.asObservable();
}
上面我正在嘗試遵循推薦的最佳做法,即返回一個 Observable,而不是從 ReplaySubject 返回。
這是相應的 unsubscribe 方法:
/**
* Unsubscribe from slice updates.
*
* @example
<pre>
slice.unsubscribe(o);
</pre>
*/
public unsubscribe(o: ReplaySubject<E[]>) {
o.unsubscribe();
this.notifyCount--;
}
我必須將 o 參數設為 ReplaySubject 類型以便進行 unsubscribe。然而,這與 subscribe 方法返回的 Observable 類型相衝突。
當嘗試像這樣測試時:
incompleteSlice.unsubscribe(incomplete$);
返回的訊息是這個:
[ts] 型別 'Observable' 的參數無法指定給型別 'ReplaySubject'。 型別 'Observable' 中缺少屬性 'scheduler'。 let incomplete$: Observable
有什麼方法可以解決這個問題嗎?
更新
一個突然浮現的明顯想法是,或許返回 asObservable 意味著我們不再需要實際從那個 observable 上 unsubscribe。我們可以讓它懸空,以防 Angular 元件被銷毀?
答案#1
我認為你混淆了從 Subscription 退訂和從 Subject 退訂。
在 Angular 中實際上建議的是在元件被銷毀時取消訂閱任何開啟的 Subscription。Subscription 是從 Observable.subscribe 返回的。當它被取消訂閱時,它不再接收來自來源 observable 的值,這在大多數情況下是你想要的效果。
從 Subject 退訂有不同的效果。它會將 Subject 轉換為 closed 狀態,您將無法再呼叫 next 或訂閱它。您可以直接查看源代碼來了解發生了什麼。
無法從 Observable 取消訂閱,這也是你會收到錯誤的部分原因。
為了遵循建議,你應該要麼列出所有的訂閱,也就是將每個訂閱加入你之後可以存取的清單中,如下所示:
this.subscriptions$.push(obs$.subscribe...))
然後在組件銷毀時,調用:
this.subscriptions$.forEach(sub -> sub.unsubscribe());
但更好且在本文中核心開發者 Ben Lesh 推薦的是,不必強制呼叫 unsubscribe,而是使用 takeUntil 運算子。
在 StackOverflow 上有一個使用這個模式與 Angular 的參考實作,你可以把它當作起點使用。
