網站搜尋

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 的參考實作,你可以把它當作起點使用。