saga 初识和使用(2)

function* loginFlow() { while(true) { const {user, password} = yield take('LOGIN_REQUEST') const token = yield call(authorize, user, password) if(token) { yield call(Api.storeItem({token})) yield take('LOGOUT') yield call(Api.clearItem('token')) } } }

若在 call 在去请求 authorize 时,结果未返回,但是此时用户又触发了 LOGOUT 的 action,此时的 LOGOUT 将会被忽略而不被处理,因为 loginFlow 在 authorize 中被堵塞了,没有执行到 take('LOGOUT')那里

同时执行多个任务

如若遇到某个场景需要同一时间执行多个任务,比如 请求 users 数据 和 products 数据, 应该使用如下的方式:

import { call } from 'redux-saga/effects' //同步执行 const [users, products] = yield [ call(fetch, '/users'), call(fetch, '/products') ] //而不是 //顺序执行 const users = yield call(fetch, '/users'), products = yield call(fetch, '/products')

当 yield 后面是一个数组时,那么数组里面的操作将按照 Promise.all 的执行规则来执行,genertor 会阻塞知道所有的 effects 被执行完成

源码解读

在每一个使用 redux-saga 的项目中,主文件中都会有如下一段将 sagas 中间件加入到 Store 的逻辑:

const sagaMiddleware = createSagaMiddleware({sagaMonitor}) const store = createStore( reducer, applyMiddleware(sagaMiddleware) ) sagaMiddleware.run(rootSaga)

其中 createSagaMiddleware 是 redux-saga 核心源码文件 src/middleware.js 中导出的方法:

export default function sagaMiddlewareFactory({ context = {}, ...options } = {}) { ... function sagaMiddleware({ getState, dispatch }) { const channel = stdChannel() channel.put = (options.emitter || identity)(channel.put) sagaMiddleware.run = runSaga.bind(null, { context, channel, dispatch, getState, sagaMonitor, logger, onError, effectMiddlewares, }) return next => action => { if (sagaMonitor && sagaMonitor.actionDispatched) { sagaMonitor.actionDispatched(action) } const result = next(action) // hit reducers channel.put(action) return result } } ... }

这段逻辑主要是执行了 sagaMiddleware(),该函数里面将 runSaga 赋值给 sagaMiddleware.run 并执行,最后返回 middleware。 接着看 runSaga() 的逻辑:

export function runSaga(options, saga, ...args) { ... const task = proc( iterator, channel, wrapSagaDispatch(dispatch), getState, context, { sagaMonitor, logger, onError, middleware }, effectId, saga.name, ) if (sagaMonitor) { sagaMonitor.effectResolved(effectId, task) } return task }

这个函数里定义了返回了一个 task 对象,该 task 是由 proc 产生的,移步 proc.js:

export default function proc( iterator, stdChannel, dispatch = noop, getState = noop, parentContext = {}, options = {}, parentEffectId = 0, name = 'anonymous', cont, ) { ... const task = newTask(parentEffectId, name, iterator, cont) const mainTask = { name, cancel: cancelMain, isRunning: true } const taskQueue = forkQueue(name, mainTask, end) ... next() return task function next(arg, isErr){ ... if (!result.done) { digestEffect(result.value, parentEffectId, '', next) } ... } }

其中 digestEffect 就执行了 effectTriggerd() 和 runEffect(),也就是执行 effect,其中 runEffect() 中定义了不同 effect 执行相对应的函数,每一个 effect 函数都在 proc.js 实现了。

除了一些核心方法之外,redux-saga 还提供了一系列的 helper 文件,这些文件的作用是返回一个类 iterator 的对象,便于后续的遍历和执行, 在此不具体分析。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/pxpjw.html