一、Saga
saga的提出最初是为了解决分布式系统中的LLT(Long Lived Transaction)
,也就是长时运行事务的数据一致性问题的。
所谓“事务(Transaction)”,指的是一个原子操作,要么全部执行,要么全部回滚。那么问题来了,为了保证数据的一致性,我们是不是应该等待刚才那个LLT执行完成呢?
以订票系统为例,我们可以把这个
LLT
拆成两个子事务嘛,T1表示“预定”事务,T2表示“出票”事务。先执行T1,然后就可以把数据库释放出来了,其他人也可以正常订票了。如果用户在30分钟内完成了付款,那么再执行T2完成出票,这样整个事务就执行完毕了。假如超过了30分钟用户还没有付款怎么办?这时候需要执行一个“补偿”事务C1,用来回滚T1对数据库造成的修改。这几个子事务组合在一起,就叫一个saga。
二、副作用(Side Effect)
显然,大多数的异步任务都需要和外部世界进行交互,不管是发起网络请求、访问本地文件或是数据库等等,因此,它们都会产生“副作用”。
三、redux-saga
redux-saga是一个Redux中间件,用来帮你管理程序的副作用。或者更直接一点,主要是用来处理异步action。
Redux中间件
在action被传递到reducer之前新进行了一次拦截,然后启动异步任务,等异步任务执行完成后再发送一个新的action,调用reducer修改状态数据。
watcher saga和worker saga
- worker saga:具体业务逻辑实现
- watcher saga:接收特定的action,然后驱动worker saga执行
import Api from '...'
function* workerSaga(action) {
try {
const user = yield call(Api.fetchUser, action.payload.userId);
yield put({type: "USER_FETCH_SUCCEEDED", user: user});
} catch (e) {
yield put({type: "USER_FETCH_FAILED", message: e.message});
}
}
function* watcherSaga() {
yield takeEvery("USER_FETCH_REQUESTED", workerSaga);
}Effect
redux saga不直接调用异步函数,而是通过
call()
或put()
这样的函数来调用,以方便测试。这些函数叫做Effect。- call:函数调用
- select:获取store中的数据
- put:向store发送action
- take:在store上等待指定action
- fork:和call类似,但是是非阻塞的,立即返回
例如上面代码中的
takeEvery
,内部就是不断地take
–>fork
–>take
–> …组合调用
// 组合为一个rootSaga
export default function* rootSaga() {
yield all([
takeEvery("FOO_ACTION", fooASaga),
takeEvery("BAR_ACTION", barASaga)
])
}import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import reducer from './reducers'
import rootSaga from './sagas'
// 创建saga
const sagaMiddleware = createSagaMiddleware()
// 在store上挂载
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
)
// 传入rootSaga,运行saga
sagaMiddleware.run(rootSaga)
参考资料: