偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Redux異步方案選型

開發(fā) 架構(gòu)
作為react社區(qū)最熱門的狀態(tài)管理框架,相信很多人都準(zhǔn)備甚至正在使用Redux。本文會(huì)從一些常見的Redux異步方案出發(fā),介紹它們的優(yōu)缺點(diǎn),進(jìn)而討論一些與異步相伴的常見場(chǎng)景,幫助你在選型時(shí)更好地權(quán)衡利弊。

作為react社區(qū)最熱門的狀態(tài)管理框架,相信很多人都準(zhǔn)備甚至正在使用Redux。

由于Redux的理念非常精簡(jiǎn),沒有追求大而全,這份架構(gòu)上的優(yōu)雅卻在某種程度上傷害了使用體驗(yàn):不能開箱即用,甚至是異步這種最常見的場(chǎng)景也要借助社區(qū)方案。

如果你已經(jīng)挑花了眼,或者正在挑但不知道是否適合,或者已經(jīng)挑了但不知道會(huì)不會(huì)有坑,這篇文章應(yīng)該適合你。

本文會(huì)從一些常見的Redux異步方案出發(fā),介紹它們的優(yōu)缺點(diǎn),進(jìn)而討論一些與異步相伴的常見場(chǎng)景,幫助你在選型時(shí)更好地權(quán)衡利弊。

簡(jiǎn)單方案

redux-thunk:指路先驅(qū)

Github:https://github.com/gaearon/redux-thunk

Redux作者Dan寫的中間件,因官方文檔出鏡而廣為人知。

它向我們展示了Redux處理異步的原理,即:

Redux本身只能處理同步的Action,但可以通過中間件來攔截處理其它類型的action,比如函數(shù)(Thunk),再用回調(diào)觸發(fā)普通Action,從而實(shí)現(xiàn)異步處理,在這點(diǎn)上所有Redux的異步方案都是類似的。

而它使用起來***的問題,就是重復(fù)的模板代碼太多: 

  1. //action types 
  2. const GET_DATA = 'GET_DATA'
  3.     GET_DATA_SUCCESS = 'GET_DATA_SUCCESS'
  4.     GET_DATA_FAILED = 'GET_DATA_FAILED'
  5.      
  6. //action creator 
  7. const getDataAction = function(id) { 
  8.     return function(dispatch, getState) { 
  9.         dispatch({ 
  10.             type: GET_DATA,  
  11.             payload: id 
  12.         }) 
  13.         api.getData(id) //注:本文所有示例的api.getData都返回promise對(duì)象 
  14.             .then(response => { 
  15.                 dispatch({ 
  16.                     type: GET_DATA_SUCCESS, 
  17.                     payload: response 
  18.                 }) 
  19.             }) 
  20.             .catch(error => { 
  21.                 dispatch({ 
  22.                     type: GET_DATA_FAILED, 
  23.                     payload: error 
  24.                 }) 
  25.             })  
  26.     } 
  27.  
  28. //reducer 
  29. const reducer = function(oldState, action) { 
  30.     switch(action.type) { 
  31.     case GET_DATA :  
  32.         return oldState; 
  33.     case GET_DATA_SUCCESS :  
  34.         return successState; 
  35.     case GET_DATA_FAILED :  
  36.         return errorState; 
  37.     } 
  38.  

這已經(jīng)是最簡(jiǎn)單的場(chǎng)景了,請(qǐng)注意:我們甚至還沒寫一行業(yè)務(wù)邏輯,如果每個(gè)異步處理都像這樣,重復(fù)且無意義的工作會(huì)變成明顯的阻礙。

另一方面,像GET_DATA_SUCCESS、GET_DATA_FAILED這樣的字符串聲明也非常無趣且易錯(cuò)。

上例中,GET_DATA這個(gè)action并不是多數(shù)場(chǎng)景需要的,它涉及我們將會(huì)提到的樂觀更新,保留這些代碼是為了和下面的方案做對(duì)比

redux-promise:瘦身過頭

由于redux-thunk寫起來實(shí)在是太麻煩了,社區(qū)當(dāng)然會(huì)有其它輪子出現(xiàn)。redux-promise則是其中比較知名的,同樣也享受了官網(wǎng)出鏡的待遇。

它自定義了一個(gè)middleware,當(dāng)檢測(cè)到有action的payload屬性是Promise對(duì)象時(shí),就會(huì):

  • 若resolve,觸發(fā)一個(gè)此action的拷貝,但payload為promise的value,并設(shè)status屬性為"success"
  • 若reject,觸發(fā)一個(gè)此action的拷貝,但payload為promise的reason,并設(shè)status屬性為"error"

說起來可能有點(diǎn)不好理解,用代碼感受下:

  1. //action types 
  2. const GET_DATA = 'GET_DATA'
  3.  
  4. //action creator 
  5. const getData = function(id) { 
  6.     return { 
  7.         type: GET_DATA, 
  8.         payload: api.getData(id) //payload為promise對(duì)象 
  9.     } 
  10.  
  11. //reducer 
  12. function reducer(oldState, action) { 
  13.     switch(action.type) { 
  14.     case GET_DATA:  
  15.         if (action.status === 'success') { 
  16.             return successState 
  17.         } else { 
  18.                return errorState 
  19.         } 
  20.     } 
  21.  

進(jìn)步巨大! 代碼量明顯減少! 就用它了! ?

請(qǐng)等等,任何能明顯減少代碼量的方案,都應(yīng)該小心它是否過度省略了什么東西,減肥是好事,減到骨頭就殘了。

redux-promise為了精簡(jiǎn)而做出的妥協(xié)非常明顯:無法處理樂觀更新

場(chǎng)景解析之:樂觀更新

多數(shù)異步場(chǎng)景都是悲觀更新(求更好的翻譯)的,即等到請(qǐng)求成功才渲染數(shù)據(jù)。而與之相對(duì)的樂觀更新,則是不等待請(qǐng)求成功,在發(fā)送請(qǐng)求的同時(shí)立即渲染數(shù)據(jù)。

最常見的例子就是微信等聊天工具,發(fā)送消息時(shí)消息立即進(jìn)入了對(duì)話窗,如果發(fā)送失敗的話,在消息旁邊再作補(bǔ)充提示即可。這種交互"樂觀"地相信請(qǐng)求會(huì)成功,因此稱作樂觀更新。

由于樂觀更新發(fā)生在用戶操作時(shí),要處理它,意味著必須有action表示用戶的初始動(dòng)作

在上面redux-thunk的例子中,我們看到了GET_DATA, GET_DATA_SUCCESS、GET_DATA_FAILED三個(gè)action,分別表示初始動(dòng)作、異步成功和異步失敗,其中***個(gè)action使得redux-thunk具備樂觀更新的能力。

而在redux-promise中,最初觸發(fā)的action被中間件攔截然后過濾掉了。原因很簡(jiǎn)單,redux認(rèn)可的action對(duì)象是 plain JavaScript objects,即簡(jiǎn)單對(duì)象,而在redux-promise中,初始action的payload是個(gè)Promise。

另一方面,使用status而不是type來區(qū)分兩個(gè)異步action也非常值得商榷,按照redux對(duì)action的定義以及社區(qū)的普遍實(shí)踐,個(gè)人還是傾向于使用不同的type,用同一type下的不同status區(qū)分action額外增加了一套隱形的約定,甚至不符合該redux-promise作者自己所提倡的FSA,體現(xiàn)在代碼上則是在switch-case內(nèi)再增加一層判斷。

redux-promise-middleware:拔亂反正

redux-promise-middleware相比redux-promise,采取了更為溫和和漸進(jìn)式的思路,保留了和redux-thunk類似的三個(gè)action。

示例:

  1. //action types 
  2. const GET_DATA = 'GET_DATA'
  3.     GET_DATA_PENDING = 'GET_DATA_PENDING'
  4.     GET_DATA_FULFILLED = 'GET_DATA_FULFILLED'
  5.     GET_DATA_REJECTED = 'GET_DATA_REJECTED'
  6.      
  7. //action creator 
  8. const getData = function(id) { 
  9.     return { 
  10.         type: GET_DATA, 
  11.         payload: { 
  12.             promise: api.getData(id), 
  13.             data: id 
  14.         } 
  15.     } 
  16.  
  17. //reducer 
  18. const reducer = function(oldState, action) { 
  19.     switch(action.type) { 
  20.     case GET_DATA_PENDING : 
  21.         return oldState; // 可通過action.payload.data獲取id 
  22.     case GET_DATA_FULFILLED :  
  23.         return successState; 
  24.     case GET_DATA_REJECTED :  
  25.         return errorState; 
  26.     } 
  27.  

如果不需要樂觀更新,action creator可以使用和redux-promise完全一樣的,更簡(jiǎn)潔的寫法,即:

  1. const getData = function(id) { 
  2.     return { 
  3.         type: GET_DATA, 
  4.         payload: api.getData(id) //等價(jià)于 {promise: api.getData(id)} 
  5.     } 
  6.  

此時(shí)初始actionGET_DATA_PENDING仍然會(huì)觸發(fā),但是payload為空。

相對(duì)redux-promise于粗暴地過濾掉整個(gè)初始action,redux-promise-middleware選擇創(chuàng)建一個(gè)只過濾payload中的promise屬性的XXX_PENDING作為初始action,以此保留樂觀更新的能力。

同時(shí)在action的區(qū)分上,它選擇了回歸type的"正途",_PENDING、_FULFILLED 、_REJECTED等后綴借用了promise規(guī)范 (當(dāng)然它們是可配置的) 。

它的遺憾則是只在action層實(shí)現(xiàn)了簡(jiǎn)化,對(duì)reducer層則束手無策。另外,相比redux-thunk,它還多出了一個(gè)_PENDING的字符串模板代碼(三個(gè)action卻需要四個(gè)type)。

社區(qū)有類似type-to-reducer這樣試圖簡(jiǎn)化reducer的庫(kù)。但由于reducer和異步action通常是兩套獨(dú)立的方案,reducer相關(guān)的庫(kù)無法去猜測(cè)異步action的后綴是什么(甚至有沒有后綴),社區(qū)也沒有相關(guān)標(biāo)準(zhǔn),也就很難對(duì)異步做出精簡(jiǎn)和抽象了。

redux-action-tools:軟文預(yù)警

無論是redux-thunk還是redux-promise-middleware,模板代碼都是顯而易見的,每次寫XXX_COMPLETED這樣的代碼都覺得是在浪費(fèi)生命——你得先在常量中聲明它們,再在action中引用,然后是reducer,假設(shè)像redux-thunk一樣每個(gè)異步action有三個(gè)type,三個(gè)文件加起來你就得寫九次!

國(guó)外開發(fā)者也有相同的報(bào)怨:

 有沒有辦法讓代碼既像redux-promise一樣簡(jiǎn)潔,又能保持樂觀更新的能力呢?

  1. const GET_DATA = 'GET_DATA'
  2.  
  3. //action creator 
  4. const getData = createAsyncAction(GET_DATA, function(id) { 
  5.     return api.getData(id) 
  6. }) 
  7.  
  8. //reducer 
  9. const reducer = createReducer() 
  10.     .when(getData, (oldState, action) => oldState) 
  11.     .done((oldState, action) => successState) 
  12.     .failed((oldState, action) => errorState) 
  13.     .build()  

redux-action-tools在action層面做的事情與前面幾個(gè)庫(kù)大同小異:同樣是派發(fā)了三個(gè)action:GET_DATA/GET_DATA_SUCCESS/GET_DATA_FAILED。這三個(gè)action的描述見下表:

type When payload meta.asyncPhase
${actionName} 異步開始前 同步調(diào)用參數(shù) 'START'
${actionName}_COMPLETED 異步成功 value of promise 'COMPLETED'
${actionName}_FAILED 異步失敗 reason of promise 'FAILED'
 

createAsyncAction參考了redux-promise作者寫的redux-actions ,它接收三個(gè)參數(shù),分別是:

  1. actionName 字符串,所有派生action的名字都以它為基礎(chǔ),初始action則與它同名
  2. promiseCreator 函數(shù),必須返回一個(gè)promise對(duì)象
  3. metaCreator 函數(shù),可選,作用后面會(huì)演示到

目前看來,其實(shí)和redux-promise/redux-promise-middleware大同小異。而真正不同的,是它同時(shí)簡(jiǎn)化了reducer層! 這種簡(jiǎn)化來自于對(duì)異步行為從語義角度的抽象:

當(dāng)(when)初始action發(fā)生時(shí)處理同步更新,若異步成功(done)則處理成功邏輯,若異步失敗(failed)則處理失敗邏輯

抽離出when/done/failed三個(gè)關(guān)鍵詞作為api,并使用鏈?zhǔn)秸{(diào)用將他們串聯(lián)起來:when函數(shù)接收兩個(gè)參數(shù):actionName和handler,其中handler是可選的,done和failed則只接收一個(gè)handler參數(shù),并且只能在when之后調(diào)用——他們分別處理`${actionName}_SUCCESS` 和 `${actionName}_FAILED`.

無論是action還是reducer層,XX_SUCCESS/XX_FAILED相關(guān)的代碼都被封裝了起來,正如在例子中看到的——你甚至不需要聲明它們!創(chuàng)建一個(gè)異步action,然后處理它的成功和失敗情況,事情本該這么簡(jiǎn)單。

更進(jìn)一步的,這三個(gè)action默認(rèn)都根據(jù)當(dāng)前所處的異步階段,設(shè)置了不同的meta(見上表中的meta.asyncPhase),它有什么用呢?用場(chǎng)景說話:

場(chǎng)景解析:失敗處理與Loading

它們是異步不可回避的兩個(gè)場(chǎng)景,幾乎每個(gè)項(xiàng)目會(huì)遇到。

以異步請(qǐng)求的失敗處理為例,每個(gè)項(xiàng)目通常都有一套比較通用的,適合多數(shù)場(chǎng)景的處理邏輯,比如彈窗提示。同時(shí)在一些特定場(chǎng)景下,又需要繞過通用邏輯進(jìn)行單獨(dú)處理,比如表單的異步校驗(yàn)。

而在實(shí)現(xiàn)通用處理邏輯時(shí),常見的問題有以下幾種:

1. 底層處理,擴(kuò)展性不足

  1. function fetchWrapper(args) { 
  2.     return fetch.apply(fetch, args) 
  3.         .catch(commonErrorHandler) 
  4.  

在較底層封裝ajax庫(kù)可以輕松實(shí)現(xiàn)全局處理,但問題也非常明顯:

一是擴(kuò)展性不足,比如少數(shù)場(chǎng)景想要繞過通用處理邏輯,還有一些場(chǎng)景錯(cuò)誤是前端生成而非直接來自于請(qǐng)求;

二是不易組合,比如有的場(chǎng)景一個(gè)action需要多個(gè)異步請(qǐng)求,但異常處理和loading是不需要重復(fù)的,因?yàn)橛脩舨恍枰酪粋€(gè)動(dòng)作有多少個(gè)請(qǐng)求。

2. 不夠內(nèi)聚,侵入業(yè)務(wù)代碼

  1. //action creator 
  2. const getData = createAsyncAction(GET_DATA, function(id) { 
  3.     return api.getData(id) 
  4.         .catch(commonErrorHandler) //調(diào)用錯(cuò)誤處理函數(shù) 
  5. })  

在有業(yè)務(wù)意義的action層調(diào)用通用處理邏輯,既能按需調(diào)用,又不妨礙異步請(qǐng)求的組合。但由于通用處理往往適用于多數(shù)場(chǎng)景,這樣寫會(huì)導(dǎo)致業(yè)務(wù)代碼變得冗余,因?yàn)閹缀趺總€(gè)action都得這么寫。

3. 高耦合,高風(fēng)險(xiǎn)

也有人把上面的方案做個(gè)依賴反轉(zhuǎn),改為在通用邏輯里監(jiān)聽業(yè)務(wù)action:

  1. function commonErrorReducer(oldState, action) { 
  2.     switch(action.type) { 
  3.     case GET_DATA_FAILED: 
  4.     case PUT_DATA_FAILED: 
  5.     //... tons of action type 
  6.         return commonErrorHandler(action
  7.     } 
  8.  

這樣做的本質(zhì)是把冗余從業(yè)務(wù)代碼中拿出來集中管理。

問題在于每添加一個(gè)請(qǐng)求,都需要修改公共代碼,把對(duì)應(yīng)的action type加進(jìn)來。且不說并行開發(fā)時(shí)merge沖突,如果加了一個(gè)異步action,但忘了往公共處理文件中添加——這是很可能會(huì)發(fā)生的——而異常是分支流程不容易被測(cè)試發(fā)現(xiàn),等到發(fā)現(xiàn),很可能就是事故而不是bug了。

通過以上幾種常見方案的分析,我認(rèn)為比較完善的錯(cuò)誤處理(Loading同理)需要具備如下特點(diǎn):

  • 面向異步動(dòng)作(action),而非直接面向請(qǐng)求
  • 不侵入業(yè)務(wù)代碼
  • 默認(rèn)使用通用處理邏輯,無需額外代碼
  • 可以繞過通用邏輯

而借助redux-action-tools提供的meta.asyncPhase,可以輕易用middleware實(shí)現(xiàn)以上全部需求!

  1. import _ from 'lodash' 
  2. import { ASYNC_PHASES } from 'redux-action-tools' 
  3.  
  4. function errorMiddleWare({dispatch}) { 
  5.   return next => action => { 
  6.     const asyncStep = _.get(action'meta.asyncStep'); 
  7.  
  8.     if (asyncStep === ASYNC_PHASES.FAILED) { 
  9.       dispatch({ 
  10.         type: 'COMMON_ERROR'
  11.         payload: { 
  12.           action 
  13.         } 
  14.       }) 
  15.     } 
  16.      
  17.     next(action); 
  18.   } 
  19.  

以上中間件一旦檢測(cè)到meta.asyncStep字段為FAILED的action便觸發(fā)新的action去調(diào)用通用處理邏輯。面向action、不侵入業(yè)務(wù)、默認(rèn)工作 (只要是用createAsyncAction聲明的異步) ! 輕松實(shí)現(xiàn)了理想需求中的前三點(diǎn),那如何定制呢?既然攔截是面向meta的,只要在創(chuàng)建action時(shí)支持對(duì)meta的自定義就行了,而createAsyncAction的第三個(gè)參數(shù)就是為此準(zhǔn)備的:

  1. import _ from 'lodash' 
  2. import { ASYNC_PHASES } from 'redux-action-tools' 
  3.  
  4. const customizedAction = createAsyncAction( 
  5.   type,  
  6.   promiseCreator, //type 和 promiseCreator此處無不同故省略 
  7.   (payload, defaultMeta) => { 
  8.     return { ...defaultMeta, omitError: true }; //向meta中添加配置參數(shù) 
  9.   } 
  10.  
  11. function errorMiddleWare({dispatch}) { 
  12.   return next => action => { 
  13.     const asyncStep = _.get(action'meta.asyncStep'); 
  14.     const omitError = _.get(action'meta.omitError'); //獲取配置參數(shù) 
  15.  
  16.     if (!omitError && asyncStep === ASYNC_PHASES.FAILED) { 
  17.       dispatch({ 
  18.         type: 'COMMON_ERROR'
  19.         payload: { 
  20.           action 
  21.         } 
  22.       }) 
  23.     } 
  24.      
  25.     next(action); 
  26.   } 
  27.  

類似的,你可以想想如何處理Loading,需要強(qiáng)調(diào)的是建議盡量用增量配置的方式進(jìn)行擴(kuò)展,而不要輕易刪除和修改meta.asyncPhase。

比如上例可以通過刪除meta.asyncPhase實(shí)現(xiàn)同樣功能,但如果同時(shí)還有其它地方也依賴meta.asyncPhase(比如loadingMiddleware),就可能導(dǎo)致本意是定制錯(cuò)誤處理,卻改變了Loading的行為,客觀來講這層風(fēng)險(xiǎn)是基于meta攔截方案的***缺點(diǎn),然而相比多數(shù)場(chǎng)景的便利、健壯,個(gè)人認(rèn)為特殊場(chǎng)景的風(fēng)險(xiǎn)是可以接受的,畢竟這些場(chǎng)景在整個(gè)開發(fā)測(cè)試流程容易獲得更多關(guān)注。

進(jìn)階方案

上面所有的方案,都把異步請(qǐng)求這一動(dòng)作放在了action creator中,這樣做的好處是簡(jiǎn)單直觀,且和Flux社區(qū)一脈相承(見下圖)。因此個(gè)人將它們歸為相對(duì)簡(jiǎn)單的一類。

下面將要介紹的,是相對(duì)復(fù)雜一類,它們都采用了與上圖不同的思路,去追求更優(yōu)雅的架構(gòu)、解決更復(fù)雜的問題

redux-loop:分形! 組合!

眾所周知,Redux是借鑒自Elm的,然而在Elm中,異步的處理卻并不是在action creator層,而是在reducer(Elm中稱update)層:

圖片來源于: https://github.com/jarvisaoie...

這樣做的目的是為了實(shí)現(xiàn)徹底的可組合性(composable)。在redux中,reducer作為函數(shù)是可組合的,action正常情況下作為純對(duì)象也是可組合的,然而一旦涉及異步,當(dāng)action嵌套組合的時(shí)候,中間件就無法正常識(shí)別,這個(gè)問題讓redux作者Dan也發(fā)出感嘆 There is no easy way to compose Redux applications并且開了一個(gè)至今仍然open的issue,對(duì)組合、分形與redux的故事,有興趣的朋友可以觀摩以上鏈接,甚至了解一下Elm,篇幅所限,本文難以盡述。

而redux-loop,則是在這方面的一個(gè)嘗試,它更徹底的模仿了Elm的模式:引入Effects的概念并將其置入reducer,官方示例如下:

  1. import { Effects, loop } from 'redux-loop'
  2. import { loadingStart, loadingSuccess, loadingFailure } from './actions'
  3.  
  4. export function fetchDetails(id) { 
  5.   return fetch(`/api/details/${id}`) 
  6.     .then((r) => r.json()) 
  7.     .then(loadingSuccess) 
  8.     .catch(loadingFailure); 
  9.  
  10. export default function reducer(state, action) { 
  11.   switch (action.type) { 
  12.     case 'LOADING_START'
  13.       return loop( 
  14.         { ...state, loading: true }, 
  15.         Effects.promise(fetchDetails, action.payload.id) 
  16.       ); // 同時(shí)返回狀態(tài)與副作用 
  17.  
  18.     case 'LOADING_SUCCESS'
  19.       return { 
  20.         ...state, 
  21.         loading: false
  22.         details: action.payload 
  23.       }; 
  24.  
  25.     case 'LOADING_FAILURE'
  26.       return { 
  27.         ...state, 
  28.         loading: false
  29.         error: action.payload.message 
  30.       }; 
  31.  
  32.     default
  33.       return state; 
  34.   } 
  35.  

注意在reducer中,當(dāng)處理LOADING_START時(shí),并沒有直接返回state對(duì)象,而是用loop函數(shù)將state和Effect"打包"返回(實(shí)際上這個(gè)返回值是數(shù)組[State, Effect],和Elm的方式非常接近)。

然而修改reducer的返回類型顯然是比較暴力的做法,除非Redux官方出面,否則很難獲得社區(qū)的廣泛認(rèn)同。更復(fù)雜的返回類型會(huì)讓很多已有的API,三方庫(kù)面臨危險(xiǎn),甚至combineReducer都需要用redux-loop提供的定制版本,這種"破壞性"也是Redux作者Dan沒有采納redux-loop進(jìn)入Redux核心代碼的原因:"If a solution doesn’t work with vanilla combineReducers(), it won’t get into Redux core"。

對(duì)Elm的分形架構(gòu)有了解,想在Redux上繼續(xù)實(shí)踐的人來說,redux-loop是很好的參考素材,但對(duì)多數(shù)人和項(xiàng)目而言,***還是更謹(jǐn)慎地看待。

redux-saga:難、而美

Github: https://github.com/yelouafi/r...

另一個(gè)著名的庫(kù),它讓異步行為成為架構(gòu)中獨(dú)立的一層(稱為saga),既不在action creator中,也不和reducer沾邊。

它的出發(fā)點(diǎn)是把副作用 (Side effect,異步行為就是典型的副作用) 看成"線程",可以通過普通的action去觸發(fā)它,當(dāng)副作用完成時(shí)也會(huì)觸發(fā)action作為輸出。

  1. import { takeEvery } from 'redux-saga' 
  2. import { call, put } from 'redux-saga/effects' 
  3. import Api from '...' 
  4.  
  5. function* getData(action) { 
  6.    try { 
  7.       const response = yield call(api.getData, action.payload.id); 
  8.       yield put({type: "GET_DATA_SUCCEEDED", payload: response}); 
  9.    } catch (e) { 
  10.       yield put({type: "GET_DATA_FAILED", payload: error}); 
  11.    } 
  12.  
  13. function* mySaga() { 
  14.   yield* takeEvery("GET_DATA", getData); 
  15.  
  16. export default mySaga;  

相比action creator的方案,它可以保證組件觸發(fā)的action是純對(duì)象,因此至少在項(xiàng)目范圍內(nèi)(middleware和saga都是項(xiàng)目的頂層依賴,跨項(xiàng)目無法保證),action的組合性明顯更加優(yōu)秀。

而它最為主打的,則是可測(cè)試性和強(qiáng)大的異步流程控制。

由于強(qiáng)制所有saga都必須是generator函數(shù),借助generator的next接口,可以輕易對(duì)異步行為的每個(gè)中間步驟做mock,實(shí)現(xiàn)對(duì)異步邏輯"step by step"的測(cè)試,這在其它方案中是很少看到的 (當(dāng)然也可以借鑒generator這一點(diǎn),但缺少約束)。

而強(qiáng)大得有點(diǎn)眼花繚亂的API,特別是channel的引入,則提供了武裝到牙齒級(jí)的異步流程控制能力。

然而,回顧我們?cè)谟懻摵?jiǎn)單方案時(shí)提到的各種場(chǎng)景與問題,redux-saga并沒有去嘗試回答和解決它們,這意味著你需要自行尋找解決方案。而generator、相對(duì)復(fù)雜的API和單獨(dú)的一層抽象也讓不少人望而卻步。

包括我在內(nèi),很多人非常欣賞redux-saga。它的架構(gòu)和思路毫無疑問是優(yōu)秀甚至優(yōu)雅的,但使用它之前,***想清楚它帶來的優(yōu)點(diǎn)(可測(cè)試性、流程控制、高度解耦)與付出的成本是否匹配,特別是異步方面復(fù)雜度并不高的項(xiàng)目,比如多數(shù)以CRUD為主的管理系統(tǒng)。

小結(jié)

本文包含了一些redux社區(qū)著名、非著名 (恩,我的redux-action-tools) 的異步方案,這些其實(shí)并不重要。

因?yàn)榉桨甘且患抑?,結(jié)論也是一家之言,不可能放之四海皆準(zhǔn)。個(gè)人更希望文中探討過的常見問題和場(chǎng)景,比如模板代碼、樂觀更新、錯(cuò)誤處理等,能夠成為你選型時(shí)的尺子,為你的權(quán)衡提供更好的參考,而不是等到項(xiàng)目熱火朝天的時(shí)候,才發(fā)現(xiàn)當(dāng)初選型的硬傷。

責(zé)任編輯:龐桂玉 來源: segmentfault
相關(guān)推薦

2012-11-14 09:42:16

Pikacode技術(shù)選項(xiàng)項(xiàng)目

2015-10-22 10:28:45

MySQL高可用方案

2021-09-26 05:03:31

數(shù)據(jù)流Redux

2020-06-12 08:22:27

React ReduxReact開發(fā)

2024-04-24 10:24:09

2021-10-19 07:27:07

邊緣集群管理

2017-12-20 16:23:18

抓娃娃

2013-03-14 11:18:30

Microsoft A解決方案

2021-02-28 13:45:12

邊緣計(jì)算云計(jì)算Kubernetes

2010-10-12 14:58:28

通信行業(yè)UPS

2021-03-31 10:16:00

架構(gòu)運(yùn)維技術(shù)

2015-08-18 08:55:03

redux核心

2023-11-03 14:32:38

2019-01-17 10:58:52

JS異步編程前端

2015-02-02 15:22:31

私有云OpenStackCloudStack

2022-08-12 11:42:44

終端管理方案UEM解決方案

2020-03-26 10:05:18

大數(shù)據(jù)IT互聯(lián)網(wǎng)

2009-07-17 09:17:41

IT運(yùn)維SiteView游龍科技

2023-05-11 07:25:57

ReduxMiddleware函數(shù)

2017-08-02 14:17:08

前端asyncawait
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)