使用redux-saga中間件處理redux中的異步

本文轉自我的博客閱讀原文荠卷。

整體感知

使用redux-saga封裝異步操作

import { call, put } from 'redux-saga/effects'
import { takeEvery } from 'redux-saga'
// 這個函數(shù)就封裝了我們的異步操作井佑,每一個yield都要等上一個yield完成之后再執(zhí)行
function* fetchData(action) {
   try {
     // 這里其實是用聲明式的方式調用Api.fetchUser方法堡僻,并傳入?yún)?shù)
      const data = yield call(Api.fetchUser, action.payload.url);
      yield put({type: "FETCH_SUCCEEDED", data});
   } catch (error) {
      yield put({type: "FETCH_FAILED", error});
   }
}
// 監(jiān)聽INCREMENT_ASYNC action的調用蒋情,然后調用fetchData這個封裝了異步的generate函數(shù)
export function* watchFetchData() {
  yield* takeEvery('FETCH_REQUESTED', fetchData)
}

watchFetchData這個Saga連接至Store

import { fetchData, watchFetchData } from './sagas'
const store = createStore(
  reducer,
  applyMiddleware(createSagaMiddleware(watchFetchData))
)

細節(jié)展示

Saga輔助函數(shù)

takeEvery是最常見的适滓,它提供了類似redux-thunk的行為仆邓;takeLatest則只執(zhí)行最后一次fetchData

import { takeEvery } from 'redux-saga'
function* watchFetchData() {
  // 只要監(jiān)聽到FETCH_REQUESTED這個action被派發(fā)暑始,就一定會調用fetchData廓握。不管上一次的fetchData有沒有完成
  yield* takeEvery('FETCH_REQUESTED', fetchData)
  // 可以作為節(jié)流函數(shù)使用搅窿,還可以避免上一次輸入已經清空,但結果還是返回了上次的輸入得到的結果
  yield* takeLatest('FETCH_REQUESTED', fetchData)
}

聲明式Effects

在 redux-saga 的世界里隙券,Sagas 都用 Generator 函數(shù)實現(xiàn)男应。我們從 Generator 里 yield 純 JavaScript 對象以表達 Saga 邏輯。 我們稱呼那些對象為 Effect娱仔。Effect 是一個簡單的對象沐飘,這個對象包含了一些給 middleware 解釋執(zhí)行的信息。 你可以把 Effect 看作是發(fā)送給 middleware 的指令以執(zhí)行某些操作(調用某些異步函數(shù)牲迫,發(fā)起一個 action 到 store)耐朴。

call

為什么我們使用call聲明式地調用一個函數(shù)而不是用“函數(shù)()”的方式?這是為了方便我們做斷言測試盹憎。
call 同樣支持調用對象方法筛峭,你可以使用以下形式,為調用的函數(shù)提供一個 this 上下文:

yield call([obj, obj.method], arg1, arg2, ...) // 如同 obj.method(arg1, arg2 ...)

apply

apply 提供了另外一種調用的方式:

yield apply(obj, obj.method, [arg1, arg2, ...])

cps

call 和 apply 非常適合返回 Promise 結果的函數(shù)脚乡。另外一個函數(shù) cps 可以用來處理 Node 風格的函數(shù) (例如蜒滩,fn(...args, callback) 中的 callback 是 (error, result) => () 這樣的形式滨达,cps 表示的是延續(xù)傳遞風格(Continuation Passing Style))。

import { cps } from 'redux-saga'
const content = yield cps(readFile, '/path/to/file')

put

同樣的俯艰,如果我們想在獲取到異步數(shù)據(jù)之后派發(fā)一個action也不能直接dispatch({ type: 'PRODUCTS_RECEIVED', products })而是要用yield put({ type: 'PRODUCTS_RECEIVED', products })這樣聲明式的調用以便測試捡遍。

錯誤處理

我們可以使用熟悉的 try/catch 語法在 Saga 中捕獲錯誤。

import Api from './path/to/api'
import { call, put } from 'redux-saga/effects'
function* fetchProducts() {
  try {
    const products = yield call(Api.fetch, '/products')
    yield put({ type: 'PRODUCTS_RECEIVED', products })
  }
  catch(error) {
    yield put({ type: 'PRODUCTS_REQUEST_FAILED', error })
  }
}

當然了竹握,你并不一定得在 try/catch 區(qū)塊中處理錯誤画株,你也可以讓你的 API 服務返回一個正常的含有錯誤標識的值。例如啦辐, 你可以捕捉 Promise 的拒絕操作谓传,并將它們映射到一個錯誤字段對象。

import Api from './path/to/api'
import { take, put } from 'redux-saga/effects'
function fetchProductsApi() {
  return Api.fetch('/products')
    .then(response => {response})
    .catch(error => {error})
}
function* fetchProducts() {
  const { response, error } = yield call(fetchProductsApi)
  if(response)
    yield put({ type: 'PRODUCTS_RECEIVED', products: response })
  else
    yield put({ type: 'PRODUCTS_REQUEST_FAILED', error })
}

高級(官方中文文檔

監(jiān)聽未來的action

之前我們有用takeEvery來對每一個相同的action進行無差別對待芹关,但其實我們也有take可以對每一次的action進行細微調控续挟。比如說,我們可以在觀察到用戶完成了3個任務之后侥衬,派發(fā)一個向用戶表示祝賀的action诗祸。

同時執(zhí)行多個任務

yield 指令可以很簡單的將異步控制流以同步的寫法表現(xiàn)出來,但與此同時我們將也會需要同時執(zhí)行多個任務轴总,我們不能直接這樣寫:

// 錯誤寫法直颅,effects 將按照順序執(zhí)行
const users = yield call(fetch, '/users'),
      repos = yield call(fetch, '/repos')
      import { call } from 'redux-saga/effects'

// 正確寫法, effects 將會同步執(zhí)行
const [users, repos] = yield [
  call(fetch, '/users'),
  call(fetch, '/repos')
]

當我們需要 yield 一個包含 effects 的數(shù)組, generator 會被阻塞直到所有的 effects 都執(zhí)行完畢怀樟,或者當一個 effect 被拒絕 (就像 Promise.all 的行為)功偿。

同時啟動多個任務,擇其優(yōu)者取值

有時候我們同時啟動多個任務往堡,但又不想等待所有任務完成械荷,我們只希望拿到 勝利者:即第一個被 resolve(或 reject)的任務。 race Effect 提供了一個方法投蝉,在多個 Effects 之間觸發(fā)一個競賽(race)养葵。
下面的示例演示了觸發(fā)一個遠程的獲取請求征堪,并且限制了 1 秒內響應瘩缆,否則作超時處理。

import { race, take, put } from 'redux-saga/effects'
function* fetchPostsWithTimeout() {
  const {posts, timeout} = yield race({
    posts   : call(fetchApi, '/posts'),
    timeout : call(delay, 1000)
  })
  if(posts)
    put({type: 'POSTS_RECEIVED', posts})
  else
    put({type: 'TIMEOUT_ERROR'})
}

race 的另一個有用的功能是佃蚜,它會自動取消那些失敗的 Effects庸娱。具體實例參看官方文檔

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谐算,隨后出現(xiàn)的幾起案子熟尉,更是在濱河造成了極大的恐慌,老刑警劉巖洲脂,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件斤儿,死亡現(xiàn)場離奇詭異剧包,居然都是意外死亡,警方通過查閱死者的電腦和手機往果,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門疆液,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人陕贮,你說我怎么就攤上這事堕油。” “怎么了肮之?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵掉缺,是天一觀的道長。 經常有香客問我戈擒,道長眶明,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任筐高,我火速辦了婚禮赘来,結果婚禮上,老公的妹妹穿的比我還像新娘凯傲。我一直安慰自己犬辰,他們只是感情好,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布冰单。 她就那樣靜靜地躺著幌缝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪诫欠。 梳的紋絲不亂的頭發(fā)上涵卵,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音荒叼,去河邊找鬼轿偎。 笑死,一個胖子當著我的面吹牛被廓,可吹牛的內容都是我干的坏晦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼嫁乘,長吁一口氣:“原來是場噩夢啊……” “哼昆婿!你這毒婦竟也來了?” 一聲冷哼從身側響起蜓斧,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤仓蛆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后挎春,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體看疙,經...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡豆拨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了能庆。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辽装。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖相味,靈堂內的尸體忽然破棺而出拾积,到底是詐尸還是另有隱情,我是刑警寧澤丰涉,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布拓巧,位于F島的核電站,受9級特大地震影響一死,放射性物質發(fā)生泄漏肛度。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一投慈、第九天 我趴在偏房一處隱蔽的房頂上張望承耿。 院中可真熱鬧,春花似錦伪煤、人聲如沸加袋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽职烧。三九已至,卻和暖如春防泵,著一層夾襖步出監(jiān)牢的瞬間蚀之,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工捷泞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留足删,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓锁右,卻偏偏與公主長得像失受,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子骡湖,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內容