做者介紹:羅雪婧,美團點評前端工程師,3年 Web 前端開發經驗,如今是美團點評點餐團隊的一員。javascript
redux-saga 是一個旨在於在React/Redux應用中更好、更易地解決異步操做(action)的庫。主要模塊是 saga 會像一個分散的支線在你的應用中單獨負責解決異步的action(相似於後臺運行的進程)。詳細移步:Redux-saga前端
redux-saga至關於在Redux原有數據流中多了一層,對Action進行監聽,捕獲到監聽的Action後能夠派生一個新的任務對state進行維護(固然也不是必需要改變State,能夠根據項目的需求設計),經過更改的state驅動View的變動。圖以下所示:java
用過redux-thunk的人會發現,redux-saga 其實和redux-thunk作的事情相似,都是能夠處理異步操做和協調複雜的dispatch。不一樣點在於:git
redux-saga-beginner-tutorialgithub
$ git clone https://github.com/HelianXJ/redux-saga-beginner-tutorial.git
$ git checkout redux-tool-saga // 切到有redux tool的分支配合chorme 的 Redux DevTools 工具查看邏輯更清晰
$ npm i //下載依賴
$ npm run hello //先看項目文件中的hello sagas複製代碼
啓動server成功後view-on: http://172.22.32.14:9966/ npm
可看到以下界面,一個簡單的例子,點擊say hello按鈕展現 hello,點擊say goodbye按鈕展現goodbye。可注意看右邊欄的Action變化和console控制檯的輸出。redux
sagas.js 關鍵代碼前端工程師
import { takeEvery } from 'redux-saga';
export function* helloSaga() {
console.log('Hello Sagas!');
}
export default function* watchIncrementAsync() {
yield* takeEvery('SAY_HELLO', helloSaga);
}複製代碼
這裏sagas建立了一個watchIncrementAsync 監聽SAY_HELLO的Action,派生一個新的任務——在控制檯打印出「Hello Sagas!」經過這例子能夠理解redux-saga大體作的事情。併發
該項目中還有一個計數器的簡單例子。異步
$ npm start //便可查看Counter的例子複製代碼
sagas.js關鍵代碼
// 一個工具函數:返回一個 Promise,這個 Promise 將在 1 秒後 resolve
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
// Our worker Saga: 將異步執行 increment 任務
export function* incrementAsync() {
yield delay(1000);
yield put({ type: 'INCREMENT' });
}
// Our watcher Saga: 在每一個 INCREMENT_ASYNC action 調用後,派生一個新的 incrementAsync 任務
export default function* watchIncrementAsync() {
yield* takeEvery('INCREMENT_ASYNC', incrementAsync);
}複製代碼
計數器例子的單元測試 sagas.spec.js 關鍵代碼
import test from 'tape';
import { put, call } from 'redux-saga/effects'
import { incrementAsync, delay } from './sagas'
test('incrementAsync Saga test', (assert) => {
const gen = incrementAsync()
assert.deepEqual(
gen.next().value,
call(delay, 1000),
'incrementAsync Saga must call delay(1000)'
)
assert.deepEqual(
gen.next().value,
put({type: 'INCREMENT'}),
'incrementAsync Saga must dispatch an INCREMENT action'
)
assert.deepEqual(
gen.next(),
{ done: true, value: undefined },
'incrementAsync Saga must be done'
)
assert.end()
});複製代碼
因爲redux-saga是用ES6的Generators實現異步,incrementAsync 是一個 Generator 函數,因此當咱們在 middleware 以外運行它,會返回一個易預見的遍歷器對象, 這一點應用在單元測試中更容易寫unit。
redux-saga能作的不僅是能夠作以上例子的事情。
實際上 redux-saga 全部的任務都通用 yield Effects 來完成。它爲各項任務提供了各類 Effect 建立器,能夠是:
function* fetchPosts() {
yield put( actions.requestPosts() )
const products = yield call(fetchApi, '/products')
yield put( actions.receivePosts(products) )
}
function* watchFetch() {
while ( yield take(FETCH_POSTS) ) {
yield call(fetchPosts) // waits for the fetchPosts task to terminate
}
}複製代碼
當 yield 一個 call 至 Generator,Saga 將等待 Generator 處理結束, 而後以返回的值恢復執行
const [users, repos] = yield [
call(fetch, '/users'),
call(fetch, '/repos')
]複製代碼
function* takeEvery(pattern, saga, ...args) {
while(true) const action = yield take(pattern)
yield fork(saga, ...args.concat(action))
}
}複製代碼