RxJS Observable:一個奇特的函式

luobotang發表於2017-09-27

前言

RxJS 的 Observable 有點難理解,其實 RxJS 相關的概念都有點難理解。畢竟 RxJS 引入了響應式程式設計這種新的模式,會不習慣是正常的。不過總得去理解嘛,而認識新的事物時,如果能夠參照一個合適的已知事物比對著,會比較容易理解吧。對於 Observable,類比 JS 中的函式,還是比較好的。

開始

封裝

先來看一個普通函式呼叫的例子:

很簡單,函式 foo() 封裝了一段邏輯(這裡只是向控制檯輸出),然後通過呼叫函式,函式執行內部的邏輯。

再來看 RxJS Observable 的一個例子:

上例中,通過 Rx.Observable.create() 來建立 Observable 物件,將同樣將一段程式碼邏輯封裝到 Observable 物件 foo 中,然後通過 foo.subscribe() 來執行封裝的程式碼邏輯。

對於普通函式和 Observable 物件,封裝的程式碼邏輯在每次呼叫時都會重新執行一次。從這一點來看,Observable 能夠和普通函式一樣實現封裝程式碼進行復用。

返回值

函式呼叫後可以有返回值:

Observable 執行後也會產生值,不過和函式直接返回的方式不同,要通過回撥函式方式獲取:

Observable 物件內部是通過 observer.next(42) 這種方式返回值,而呼叫方則通過回撥函式來接收返回的資料。形式上比普通函式直接返回值囉嗦一些。

從呼叫方的角度來看,兩個過程分別是:

  • 普通函式:呼叫 > 執行邏輯 > 返回資料
  • Observable:訂閱(subscribe) > 執行邏輯 > 返回資料

從獲取返回值方式來看,呼叫函式是一種直接獲取資料的模式,從函式那裡“拿”(pull)資料;而 Observable 訂閱後,是要由 Observable 通過間接呼叫回撥函式的方式,將資料“推”(push)給呼叫方。

這裡 pull 和 push 的重要區別在於,push 模式下,Observable 可以決定什麼時候返回值,以及返回幾個值(即呼叫回撥函式的次數)。

上面例子中,Observable 返回了兩個值,第1個值同步返回,第2個值則是過了1秒後非同步返回。

也就是說,從返回值來說,Observable 相比普通函式區別在於:

  • 可以返回多個值
  • 可以非同步返回值

異常處理

函式執行可能出現異常情況,例如:

我們可以捕獲到異常狀態進行處理:

對於 Observable,也有錯誤處理的機制:

Observable 的 subscribe() 方法支援傳入額外的回撥函式,用於處理異常情況。和函式執行類似,出現錯誤之後,Observable 就不再繼續返回資料了。

subscribe() 方法還支援另一種形式傳入回撥函式:

而這種形式下,傳入的物件和 Observable 內部執行函式中的 observer 引數在形式上就比較一致了。

中止執行

Observable 內部的邏輯可以非同步多個返回值,甚至返回無數個值:

上面例子中,Observable 物件每隔 1 秒會返回一個值給呼叫方。即使呼叫方不再需要資料,仍舊會繼續通過回撥函式向呼叫推送資料。

RxJS 提供了中止 Observable 執行的機制:

subscribe() 方法返回一個訂閱物件(subscription),該物件上的 unsubscribe() 方法用於取消訂閱,也就是中止 Observable 內部邏輯的執行,停止返回新的資料。

對於具體的 Observable 物件是如何中止執行,則要由 Observable 在執行後返回一個用於中止執行的函式,像上面例子中的這種方式。

Observable 執行結束後,會觸發觀察者的 complete 回撥,所以可以這樣:

Observable 的觀察者共有上面三種回撥:

  • next:獲得資料
  • error:處理異常
  • complete:執行結束

其中 next 可以被多次呼叫,error 和 complete 最多隻有一個被呼叫一次(任意一個被呼叫後不再觸發其他回撥)。

資料轉換

對於函式返回值,有時候我們要轉換後再使用,例如:

對於 Observable 返回的值,也會有類似的情況,不過通常採用下面的方式:

其實 foo.map() 返回了新的 Observable 物件,上面程式碼等價於:

Observable 物件 foo2 被訂閱時執行的內部邏輯可以簡單視為:

將這種對資料的處理和陣列進行比較看看:

是不是有點像?

除了 map() 方法,Observable 還提供了多種轉換方法,如 filter() 用於過濾資料,find() 值返回第一個滿足條件的資料,reduce() 對資料進行累積處理,在執行結束後返回最終的資料。這些方法和陣列方法功能是類似的,只不過是對非同步返回的資料進行處理。還有一些轉換方法更加強大,例如可以 debounceTime() 可以在時間維度上對資料進行攔截等等。

Observable 的轉換方法,本質不過是建立了一個新的 Observable,新的 Observable 基於一定的邏輯對原 Observable 的返回值進行轉換處理,然後再推送給觀察者。

總結

Observable 就是一個奇怪的函式,它有和函式類似的東西,例如封裝了一段邏輯,每次呼叫時都會重新執行邏輯,執行有返回資料等;也有更特殊的特性,例如資料是推送(push)的方式返回給呼叫方法,返回值可以是非同步,可以返回多個值等。

不過將 Observable 視作特殊函式,至少對於理解 Observable 上是比較有幫助的。

Observable 也被視為 data stream(資料流),這是從 Observable 可以返回多個值的角度來看的,而資料轉換則是基於當前資料流建立新的資料流,例如:

observable.map(x => 10 * x)

observable.map(x => 10 * x)

不過上圖看到的只是資料,而將 Observable 視為特殊函式時,不應該忘了其內部邏輯,不然資料是怎麼產生的呢。

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式

RxJS Observable:一個奇特的函式 RxJS Observable:一個奇特的函式

相關文章