做者介绍:罗雪婧,美团点评前端工程师,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))
}
}复制代码