RxJS原來應該這樣用!

引言

最近幫潘佳琦解決了一個詭異的問題,而後忽然發現⾃己對觀察者感到迷茫了。typescript

clipboard.png

需求是⼀個註銷按鈕,若是是技術機構登錄,就調用技術機構的註銷⽅法,若是是器具用戶登錄,就調⽤器具⽤戶的註銷方法。固然,最優的解決⽅案並不是我下⽂所列的,既然功能不同,那就應該是兩個對象。看來咱們的⾯向對象運用得還不不夠靈活。網絡

原問題解決

問題描述

註銷代碼以下,只表達思想,別去深究具體的語法:架構

logout(): void {
    this.departmentService.isLogin$.subscribe((isDepartmentLogin) => {
        if (isDepartmentLogin) {
            this.departmentLogout();
        }
    });

    this.systemService.isLogin$.subscribe((isTechnicalLogin) => {
        if (isTechnicalLogin) {
            this.technicalLogout();
        }
    });
}

看着好像沒啥錯誤啊?訂閱獲取當前登陸用戶狀態,若是departmentService.isLogin$true,表示是器具用戶登陸,則調用器具用戶的註銷方法;若是是systemService.isLogin$true,表示是技術機構登陸,則調用技術機構的註銷方法。框架

然而,詭異的事情發生了:微服務

  1. 首次打開系統,登陸,登陸成功。
  2. 點擊註銷,也能註銷成功。
  3. 可是再登陸系統,就登不進去了。

也就是說,這個註銷方法影響了後續的登陸,當時就很懵圈,爲何呢?性能

後來多打了幾條日誌,才發現了問題所在:this

緣由分析

根據Spring官方的Spring Security and Angular所述,官方作法是用一個boolean值來判斷當前是否登陸,從而進行視圖的展現。spa

clipboard.png

潘老師在新系統中也是根據官方的推薦進行實現的:設計

let isLogin$ = new BehaviorSubject<boolean>();

login() {
    ......
    this.isLogin$.next(true);
}

logout() {
    ......
    this.isLogin$.next(false);
}

一個可觀察的boolean對象來判斷當前用戶是否登陸,而後main組件訂閱這個值,根據是否登陸並結合ngIf來判斷當前應該顯示登陸組件仍是應用組件。3d

clipboard.png

看這個圖你們應該就明白了,問題出在了對subscribe的理解上。

點擊註銷,發起訂閱,當isLogin$true的時候就註銷,註銷成功。

下次登陸時,這個訂閱還在呢!而後點擊登陸,執行登陸邏輯,將isLogin$設置爲true,觀察者就通知了訂閱者,又執行了一遍註銷邏輯。確定登不上去了。

執行訂閱以後,應該獲取返回值,再執行取消訂閱。

迷茫

因此,這個問題出在本該訂閱一次,可是subscribe是隻要訂閱過了,在取消訂閱以前,我一直是這個可觀察對象的觀察者。

想到這我就迷茫了,就是我一訂閱,一直到我取消訂閱以前,這個可觀察對象都要維護着觀察者的列表。

那咱們的網絡請求用的不也是Observable嗎?只是此Observable是由HttpClient構建好返回給咱們的。那咱們訂閱了也沒取消,那它是否是一直維護着這個關係,會不會有性能問題?難道咱們以前的用法都錯了嗎?

這個問題一直困擾了我好多天,知道今天才在Angular官網上看到相關的介紹,才解決了個人迷茫。

clipboard.png

HttpClient.get()方法正常狀況下只會返回一個可觀察對象,它或者發出數據,或者發出錯誤。有些人說它是「一次性完成」的可觀察對象。

The HttpClient.get() method normally returns an observable that either emits the data or an error. Some folks describe it as a "one and done" observable.

或許英文文檔描述得更準確,one and doneHttpClient返回的Observable對象只執行一次,而後就銷燬。也就是所說的執行一次,或者是next或者是error,而後執行complete銷燬這個對象。

Angular應該早就想到了維護觀察者帶來的性能問題,才設計一次性的觀察者對象,我卻是杞人憂天了。

拓展RxJS

一次訂閱解決方案

需求不一樣,有時咱們須要一直訂閱,有時咱們卻想只訂閱一次,不然就會發生一些很詭異的問題。

可是Observable只提供了一個subscribe方法,想訂閱一次得手動取消。我想若是Observable中再添加一個subscribeOnce的方法,那開發起來會不會比如今更順手?

clipboard.png

Google了一下,真的有啊!StackOverflow上這老哥和我同樣的想法,有沒有相似subscribeOnce這樣式的方法?

clipboard.png

回答得很棒,舊版本流式調用使用first方法,新版本使用pipe,裏面再調用first方法。

同時這裏說了,若是first的條件不符合時,會自動取消訂閱。

Angular中取消訂閱

關於如何在Angular中最優雅地實現取消訂閱,請參考這個問題:Angular/RxJs When should I unsubscribe from Subscription - StackOverflow

clipboard.png

八百多個贊,回答得很是好,惋惜我看得是一臉懵逼,可能我開發經驗不夠。您感興趣能夠去看看原回答的實現方式。

若是您有任何的意見或建議,歡迎批評指正。

總結

世上最難的不是學一門技術,而是如何實踐。

沒有教科書,沒有項目參考,全靠一份官方文檔,每一個人都創造着本身的最佳實踐。

一路走來,從華軟開始,也得四個月了,到如今也沒設計出一款滿意的前臺架構。

優秀架構師的設計經驗,確定不會分享給你,而普通的Angular書籍,不過是一個小的Demo項目,徹底沒有考慮過項目很龐大的時候應該怎麼組織架構。

最近發現了基於Angular的前臺微服務框架Mooa,最近沒時間之後再細看,但願它不要再讓我失望。

相關文章
相關標籤/搜索