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 的核心原理是:

  1. 命令式副作用管理:不直接执行,而是 yield 描述对象
  2. Generator 驱动:外部执行器控制 Generator 的暂停和恢复
  3. 事件驱动架构:通过 channel 监听和分发 action
  4. 任务并发模型:类似操作系统进程的 fork/join 模型

这种设计使得副作用管理变得 可测试、可回溯、可协作,这也是它相比其他中间件的最大优势。

posted @ 2026-03-11 09:29  箫笛  阅读(1)  评论(0)    收藏  举报