在學習redux-saga以前,咱們必須瞭解ES6 Generators [ES6 Generators]html
中間件:將具體業務和底層邏輯解耦的組件。redux中,中間件就是一個函數,對store.dispatch方法進行了改造,在發出 Action 和執行 Reducer 這兩步之間,添加了其餘功能。 redux
附上官網redux-中間件介紹
api
redux應用中最經常使用的兩種異步流處理方式: redux-thunk,redux-sagapromise
redux-saga 是一個用於管理 Redux 應用異步操做(Side Effects。譯註:直譯成 「反作用」 不太通順,因此這裏譯爲 「異步操做」 更好理解)的中間件(又稱異步 action)。 redux-saga 經過建立 Sagas 將全部的異步操做邏輯收集在一個地方集中處理,能夠用來代替 redux-thunk 中間件。
bash
它具備以下特性:
babel
集中處理 redux 反作用問題。架構
被實現爲 generator 。併發
類 redux-thunk 中間件。app
watch/worker(監聽->執行) 的工做形式。異步
gen.next() // => { done: ..., value: ... }
)作 deepEqual 測試)redux-saga [官方文檔]有詳細的使用說明
redux-saga [中文文檔]
(經常使用)
Saga Helpers
Effect creators
const id = yield select(state => state.id);複製代碼
Effect combinators
Interfaces
fork
,middleware.run
或 runSaga
執行 Saga 的結果。redux-saga API詳解能夠查看 [API 參考]
Effect 是一個簡單的對象,該對象包含了一些給 middleware 解釋執行的信息。能夠經過使用 effects API 如 fork,call,take,put,cancel
等來建立 Effect。( redux-saga [API 參考])
觸發一個action 到 reducer的過程當中,如 yield call(delay, 1000)
即 yield 了下面的對象,call 建立了一條描述結果的信息,而後,redux-saga middleware
將確保執行這些指令並將指令的結果返回給 Generator
從 Saga 內觸發異步操做(Side Effect)老是由 yield 一些聲明式的 Effect 來完成的 (你也能夠直接 yield Promise,可是這會讓測試變得困難。使用 Effect 諸如 call 和 put,與高階 API 如 takeEvery 相結合,又有額外的易於測試的好處。
redux-saga 提供了另一個函數 put,這個函數用於建立 dispatch Effect
yield put({ type: '',... })
yield關鍵字用來暫停和恢復一個生成器函數,yield關鍵字使生成器函數執行暫停,yield關鍵字後面的表達式的值返回給生成器的調用者。它能夠被認爲是一個基於生成器的版本的return關鍵字。
yield關鍵字實際返回一個IteratorResult對象,它有兩個屬性,value和done。value屬性是對yield表達式求值的結果,而done是false,表示生成器函數還沒有徹底完成。
try/catch
語法在 Saga 中捕獲錯誤
Redux Saga能夠理解爲一個和系統交互的常駐進程,其中,Saga可簡單定義以下:
Sagas經過Generator函數來建立,採用 Generator 函數來 yield Effects,只會在應用啓動時調用,咱們能夠把它看做是在後臺運行的進程,Sagas監聽發起的action,而後決定基於這個action來作什麼,是發起異步調用仍是發起其餘的action到Store,甚至是調用其餘的Sagas
Watcher/Worker 指的是一種使用兩個單獨的 Saga 來組織控制流的方式。
1. root saga:當即啓動 sagas 的惟一入口
2. Watcher: 監聽發起的 action 並在每次接收到 action 時 fork 一個 worker。
3. Worker: 處理 action 並結束它。
eg:
//動態執行 rootSaga。用於 applyMiddleware 階段以後執行 rootSaga。
middleware.run(rootSaga, ...args)
...
//Watcher/Worker
function* watcher() {
while(true) {
const action = yield take(ACTION)
yield fork(worker, action.payload)
}
}
function* worker(payload) {
// ... do some stuff
}
複製代碼
try {
dispatch({ type: LOGIN_REQUEST });
let { data } = await request.post('/login', { user, password });
await dispatch(loadUserData(data.uid));
dispatch({ type: LOGIN_SUCCESS, data });
} catch(error) {
dispatch({ type: LOGIN_ERROR, error });
}
}
export const loadUserData = (uid) => async (dispatch) => {
try {
dispatch({ type: USERDATA_REQUEST });
let { data } = await request.get(`/users/${uid}`);
dispatch({ type: USERDATA_SUCCESS, data });
} catch(error) {
dispatch({ type: USERDATA_ERROR, error });
}
}
複製代碼
export function* loginSaga() {
while(true) {
const { user, pass } = yield take(LOGIN_REQUEST) //等待 Store 上指定的 action LOGIN_REQUEST
try {
let { data } = yield call(request.post, '/login', { user, pass }); //阻塞,請求後臺數據
yield fork(loadUserData, data.uid); //非阻塞執行loadUserData
yield put({ type: LOGIN_SUCCESS, data }); //發起一個action,相似於dispatch
} catch(error) {
yield put({ type: LOGIN_ERROR, error });
}
}
}
export function* loadUserData(uid) {
try {
yield put({ type: USERDATA_REQUEST });
let { data } = yield call(request.get, `/users/${uid}`);
yield put({ type: USERDATA_SUCCESS, data });
} catch(error) {
yield put({ type: USERDATA_ERROR, error });
}
}
複製代碼
1. 在組件中,再也不dispatch(action creator),而是dispatch(pure action)
2. 組件中再也不關注由誰來處理當前action,action經由root saga分發
3. 具體業務處理方法中,經過提供的call/put等幫助方法,聲明式的進行方法調用
4. 使用ES6 Generator語法,簡化異步代碼語法 5. redux-saga 將異步任務進行了集中處理,且方便測試。
redux-saga的使用更多的是根據我的需求與習慣
1. 聲明式 Effects:全部的操做以JavaScript對象的方式被 yield,並被 middleware 執行。使得在 saga 內部測試變得更加容易,能夠經過簡單地遍歷 Generator 並在 yield 後的成功值上面作一個 deepEqual 測試。
2. 高級的異步控制流以及併發管理:可使用簡單的同步方式描述異步流,並經過 fork 實現併發任務。
3. 架構上的優點:將全部的異步流程控制都移入到了 sagas,UI 組件不用執行業務邏輯,只需 dispatch action 就行,加強組件複用性。
目前在項目實踐中遇到的一些問題:
1. redux-saga 不強迫咱們捕獲異常,這每每會形成異常發生時難以發現緣由。所以,一個良好的習慣是,相信任何一個過程都有可能發生異常。
2. generator 的調試環境比較糟糕,babel 的 source-map 常常錯位,常常要手動加 debugger 來調試。
4. 在action的定義上要謹慎,避免action在saga和reducer之間重複觸發,形成死循環