sagas背景
- Sagas are Long Lived Transactions
- Sagas are a lot of small transactions
- Sagas are a Failure Management Pattern
redux-sagas是专门用来处理副作用的,这样redux及react的其他部分就可以保持pure的状态
栗子:
import { delay } from 'redux-saga'
import { put, takeEvery } from 'redux-saga/effects'
// ...
// Our worker Saga: 将执行异步的 increment 任务
export function* incrementAsync() {
yield delay(1000)
yield put({ type: 'INCREMENT' })
}
// Our watcher Saga: 在每个 INCREMENT_ASYNC action spawn 一个新的 incrementAsync 任务
export function* watchIncrementAsync() {
yield takeEvery('INCREMENT_ASYNC', incrementAsync)
}
sagas使用generator
而不是await
来等待异步函数,
因为这样可以给调用方机会去cancel
掉后续的执行,
每一个yield
都是一个中断的机会
takeEvery做了什么
查看这里
我们知道它fork
了一个worker
export function takeEvery(patternOrChannel, worker, ...args) {
if (process.env.NODE_ENV !== 'production') {
validateTakeEffect(takeEvery, patternOrChannel, worker)
}
return fork(takeEveryHelper, patternOrChannel, worker, ...args)
}
sagas
内建了一个状态机,
用来持续执行take
fork
这两个动作
return fsmIterator(
{
q1() {
return { nextState: 'q2', effect: yTake, stateUpdater: setAction }
},
q2() {
return { nextState: 'q1', effect: yFork(action) }
},
},
'q1',
`takeEvery(${safeName(patternOrChannel)}, ${worker.name})`,
)
这个地方有点类似于尾递归优化的Trampoline,
将递归转换成了状态机进行管理,
take
生成匹配的声明yFork
生成执行generator
的声明
call, apply from sagas
import { call } from 'redux-saga/effects'
function* fetchProducts() {
const products = yield call(Api.fetch, '/products')
// ...
}
function* fetchProducts() {
const products = yield apply(Api, Api.fetch, ['/products'])
// ...
}
import { cps } from 'redux-saga'
const content = yield cps(readFile, '/path/to/file')
这几个都是生命式调用,让sagas
在middleware
中进行调用,
方便进行测试
put
类似call,但是是用来声明式dispatch
action
的