在学习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之间重复触发,形成死循环