Redux 異步 Action 解決二式 (Observable 法)

miZyind
miZyind Singularity
6 min readJul 15, 2020

--

Redux +ReactiveX = Redux-Observable

前情提要

先前已於「一式」介紹過 redux-thunk
它透過增強原生 Redux dispatch 方法來處理異步 Action
原生僅支援這種呼叫方式:dispatch([Plain Object])
增強後則可將「方法」也傳進去,呼叫式可寫為:dispatch([Function])

因此開發者可以將整個異步 Action 處理流程組合在單一方法中
再來只需循著 Redux 的原始邏輯而走,由它去更新中央 store
這種方式不需要理解太多背景知識,僅需熟悉 Redux 本身的原理
以及一些程式語言基礎函式的寫法即可

然而前端的需求千變萬化
如果只實作撈資料「成功」「失敗」等基礎邏輯,已不太能滿足需求
有時我們希望資料還沒撈完就按下「停止」
甚至想在撈之前先「檢查後端伺服器的繁忙度」
如果後端太忙就傳少點資料
使用 redux-thunk 雖然可以達成同樣需求
但它「將整個異步 Action 處理流程組合在單一方法」的特性
反倒變成一種限制,限制我們無法輕鬆的將各個異步呼叫拆開
舉例來說,我們有 A, B, C 三個異步呼叫需組合
這個組合完的方法大致長這樣:

在 Redux 處理這段 dispatch(fetchABCThunk()) 時,前端無論做什麼操作
都無權控制 A, B, C 三個異步呼叫何時被觸發,甚至中途想終止也不行

於是「二式」利用了 redux-observable 來改寫異步 Action 的處理流程
就讓我們來感受下「一式 Thunk」「二式 Observable」寫起來的差異!

旁觀者清,Reactive 導向的程式設計

古人有云:「正是當局者迷,旁觀者清」
redux-observable 就是幫助我們以「旁觀者」的角度梳理整個流程
不過,在學習並實際使用它之前,得先理解一下什麼是 ReactiveX(Rx)

它是個學習曲線較陡峭,但跨語言且跨前後端的概念型類別庫
就如同 Redux 一樣,只是個「概念」
在學會 Redux 後,我們不僅能在 React 內用到
開發後端需要「管理狀態」時也管用
當然也能套用到其他前端框架,如 Angular, Vue
Rx 也是如此,它幾乎在各個主流程式語言中都有實作版本
無論前端或後端,也都脫離不了異步流程的處理
如果能掌握 Rx 的概念,在開發類似流程時將非常有幫助!
本篇使用的是在 JavaScript 中的實作:RxJS
在這就不多做 RxJS 的介紹,僅將重點放在 Thunk 與 Observable 的差異

重構,Thunk 至 Epic

首先再次重提 Redux 是由 Action, Reducer, Store 三元素所構成
然而在 redux-observable 中則又多了一個稱為「Epic」的元素
直翻「史詩」的話聽起來很潮,一種離我們很遙遠的感覺
但它其實只是幫我們將異步 Action 處理流程設計如「詩」
讓我們得以細細「閱讀」且不影響原先 Redux 的所有邏輯
重構 Thunk 至 Epic 後,異步呼叫流程如下:

看到這你可能會覺得…這什麼鬼啊完全難以閱讀嘛!
redux-observable 就這點能耐、Callback Hell 再現!?
這時就不得不來幹古一下 XD

唐朝的「格律詩」在押韻平仄甚至對仗等方面都有嚴格的要求
詩人們巧妙的運用著語言特性,而創造了許多流傳千古的名詩
我們平常隨口說幾個句子,或許幾小時甚至幾分鐘後
就難以字字不漏的原句重現
但是脫口說一句「床前明月光」相信大部分人都能將後三句接完
甚至目前旅居在越南的我,所認識一位文學造詣很高的越南同事
也能將此詩以越文朗誦出來!
當時初次知道越南人也學李白的詩,真的很吃驚!

可見格律詩的影響甚廣!

RxJS 中的各個 operator,就像為異步流程加上押韻平仄與對仗
上方第一個重構範例很粗暴的把每段詩句串在一起,不是很好看
但當我們仔細閱讀
會發現大多都是重複的押韻 (from, of, switchMap)
根據這個特性來試著將它們拆分:

可以發現拆完後變為「三個方法」且各方法僅需處理「一個異步呼叫」
無論是要修改各自的執行邏輯,甚至個別加入單元測試
都比起修改 Thunk 那一大包的「單一方法」更容易

至於要怎麼觸發這一系列的 Epics 呢?它們又是怎麼依序進行的?
由於 redux-observable 並沒對 Redux 的 dispatch 方法做任何修改
呼叫式就是原本的 dispatch([Plain Object])
因此僅需以底下的,將 Action 傳進去的方式來調用:

上面三個 Epics 即會神奇的依序被觸發了,流程如下:

上方每一個 dispatch 的參數類型都跟原版的 Redux 一模一樣
與「一式 Thunk」的 store.dispatch(fetchProductsThunk) 不同
redux-observable 巧妙利用了 Redux + RxJS 的原始特性來梳理異步流程
這樣的優點是,即使有一天你不再使用它,想用別套異步處理套件
也僅需要移除該套件與 Epics
原本 component 中調用 dispatch 的部份都不必修改,降低依賴性!

如何套用 middleware 至 Redux?

可以從 redux-observable 的 官方文件 找到說明:

完整範例

這是為了本文所寫的 CodeSandbox 完整範例
內含了粗暴型 fetchABCEpic
以及拆分過後的 fetchAEpic, fetchBEpic, fetchCEpic
可以自己改改玩玩看
感受一下 redux-observable 帶來的全新思考方式!

--

--