Redux - redux-saga 核心原理
Redux-Saga 本质上是一个 基于 Generator 函数 + 事件监听 的 Redux 中间件,它的核心原理可以从以下几个维度深入理解:
1. 底层架构原理
1.1 中间件机制
// Redux-Saga 中间件的简化实现
const sagaMiddleware = store => next => action => {
// 1. 将 action 分发到 saga 通道
sagaChannel.put(action)
// 2. 继续传递 action 给下一个中间件
const result = next(action)
return result
}
核心作用:拦截所有 Redux action,同时创建一个与 Redux 并行的"副作用执行环境"
1.2 基于 Generator 的控制反转
// 普通函数 vs Generator
function* exampleSaga() {
// 实际并不在这里执行异步操作
const data = yield call(api.fetchUser)
// 这里的赋值是在中间件中完成的
yield put({ type: 'SUCCESS', data })
}
关键原理:Generator 函数可以被外部控制,每次 yield 都会暂停执行,将控制权交给 saga 中间件
2. 核心执行流程
2.1 Effect 创建器
// Effect 的本质 - 普通对象
const callEffect = {
type: 'CALL',
fn: api.fetchUser,
args: [1]
}
const putEffect = {
type: 'PUT',
action: { type: 'SUCCESS', payload: {} }
}
// redux-saga/effects 导出的是工厂函数
function call(fn, ...args) {
return { type: 'CALL', fn, args }
}
原理:yield call() 返回的不是执行结果,而是一个 Effect 描述对象
2.2 Saga 执行器
// 简化的 saga 执行器
function runSaga(generator, store) {
const iterator = generator()
function next(effect) {
const { value, done } = iterator.next(effect)
if (done) return
// 根据 Effect 类型执行不同操作
switch (value.type) {
case 'CALL':
Promise.resolve(value.fn(...value.args))
.then(result => next(result))
break
case 'PUT':
store.dispatch(value.action)
next()
break
case 'TAKE':
// 监听特定 action
store.subscribe(action => {
if (action.type === value.actionType) {
next(action)
}
})
break
}
}
next()
}
3. 任务管理机制
3.1 任务队列与 fork 模型
// 简化的任务管理系统
class TaskManager {
constructor() {
this.tasks = new Map()
this.mainTask = null
}
fork(saga, ...args) {
const iterator = saga(...args)
const task = {
iterator,
cancel: () => this.cancelTask(task)
}
// 非阻塞执行
this.runTask(iterator)
return task
}
async runTask(iterator) {
let result = { done: false }
while (!result.done) {
try {
const { value, done } = iterator.next()
result = { value, done }
if (value && value.type === 'CALL') {
// 阻塞执行
result.value = await value.fn(...value.args)
}
} catch (error) {
iterator.throw(error)
}
}
}
}
3.2 通道机制 (Channel)
// 简化的 channel 实现
class Channel {
constructor() {
this.listeners = []
this.buffer = []
}
take(pattern) {
return {
type: 'TAKE',
pattern,
channel: this
}
}
put(action) {
return {
type: 'PUT',
action,
channel: this
}
}
// 内部调度
dispatch(action) {
this.buffer.push(action)
this.listeners.forEach(listener => listener(action))
}
}
4. 完整的执行流程
// Redux-Saga 完整执行流程示意图
// 1. 初始化阶段
store = createStore(reducer, applyMiddleware(sagaMiddleware))
sagaMiddleware.run(rootSaga)
// 2. Saga 执行阶段
function* rootSaga() {
// 创建 Effect 对象
const effect = yield takeEvery('FETCH_USER', fetchUserSaga)
// effect = { type: 'FORK', saga: fetchUserSaga, pattern: 'FETCH_USER' }
}
// 3. Effect 处理阶段
// Saga 中间件内部逻辑:
function processEffect(effect, store) {
switch(effect.type) {
case 'FORK':
// 非阻塞执行
const task = runSaga(effect.saga, store)
return task
case 'TAKE':
// 创建监听器
return new Promise(resolve => {
const unsubscribe = store.subscribe(() => {
const action = store.getState().lastAction
if (action.type === effect.pattern) {
unsubscribe()
resolve(action)
}
})
})
case 'CALL':
// 阻塞执行
return effect.fn(...effect.args)
case 'PUT':
// 触发 action
return store.dispatch(effect.action)
}
}
// 4. 反向注入阶段
// Saga 中 yield 的表达式会在中间件执行后获得实际值
const data = yield call(api.fetchUser) // data 是实际 API 返回结果
5. 核心设计模式
5.1 命令模式
// Effect 本质是命令对象
const command = {
type: 'CALL',
execute: async () => {
return await api.fetchUser()
},
args: []
}
// 中间件是命令执行者
middleware.execute(command).then(result => {
saga.next(result) // 恢复 Generator 执行
})
5.2 观察者模式
class SagaObserver {
constructor(actionTypes) {
this.actionTypes = actionTypes
}
// take effect 的底层实现
take(pattern) {
return new Promise(resolve => {
this.actionEmitter.on(pattern, resolve)
})
}
}
5.3 迭代器模式
// Generator 函数返回迭代器
const iterator = saga()
// 外部控制迭代
const effect1 = iterator.next().value // yield call()
const effect2 = iterator.next(data).value // yield put()
6. 性能优化机制
6.1 缓冲区管理
// action 缓冲
class ActionBuffer {
constructor(limit = 10) {
this.queue = []
this.limit = limit
}
put(action) {
if (this.queue.length < this.limit) {
this.queue.push(action)
}
}
take() {
return this.queue.shift()
}
}
6.2 任务取消机制
// 取消令牌
class CancellationToken {
constructor() {
this.isCancelled = false
}
cancel() {
this.isCancelled = true
}
throwIfCancelled() {
if (this.isCancelled) {
throw new SagaCancellationError()
}
}
}
7. 与 Redux-Thunk 的核心区别
// Redux-Thunk: 函数执行
const thunkAction = (id) => {
return async (dispatch) => {
dispatch({ type: 'LOADING' })
const data = await api.fetch(id)
dispatch({ type: 'SUCCESS', data })
}
}
// Redux-Saga: 命令执行
function* sagaAction(id) {
yield put({ type: 'LOADING' })
const data = yield call(api.fetch, id)
yield put({ type: 'SUCCESS', data })
}
本质区别:
- Thunk:把异步逻辑写在 action creator 里,直接执行
- Saga:把异步逻辑写成命令描述,由中间件解析执行
总结
Redux-Saga 的核心原理是:
- 命令式副作用管理:不直接执行,而是 yield 描述对象
- Generator 驱动:外部执行器控制 Generator 的暂停和恢复
- 事件驱动架构:通过 channel 监听和分发 action
- 任务并发模型:类似操作系统进程的 fork/join 模型
这种设计使得副作用管理变得 可测试、可回溯、可协作,这也是它相比其他中间件的最大优势。

浙公网安备 33010602011771号