Saga 模式通过 Generator 函数实现可读、可测试、可中断的异步流程管理,核心是 yield 暂停并返回 Effect 描述操作,由中间件执行;支持监听、取消、竞态处理与流程编排。

JavaScript 中的 Saga 模式通过 Generator 函数实现对复杂异步流程(如多步骤请求、错误恢复、并发控制、取消操作)的可读、可测试、可中断的管理。它不直接处理 Promise,而是把异步逻辑“暂停”在 yield 处,由中间件(如 redux-saga)接管执行与调度。
Generator 是 Saga 的核心执行载体
Generator 函数用 function* 声明,配合 yield 暂停执行,并返回一个迭代器对象。Saga 就是一系列被 yield 暂停的 Effect(如 call、put、take),它们本身不执行异步操作,只描述“要做什么”。
- yield call(api.fetchUser, id) —— 描述“调用 fetchUser 并等待结果”,实际执行由 saga middleware 完成
- yield take('USER_LOGIN_REQUEST') —— 描述“等待某个 action 被 dispatch”,挂起直到匹配
- yield race({ login: call(loginApi), timeout: delay(5000) }) —— 描述“登录或超时,任一完成即结束”
Saga 解决传统异步方案的痛点
对比 async/await 或纯 Promise 链,Saga 在以下场景更清晰可控:
- 监听全局动作:比如在任意时刻收到 LOGOUT,立即取消所有进行中的请求(通过 fork + cancel 实现)
- 多路分支与竞态处理:用户快速切换 tab,前一个请求应自动取消,避免状态错乱(takeLatest 效果)
- 流程编排与重试:登录失败后自动弹窗 → 等待用户点击重试 → 最多重试 3 次 → 失败则跳转错误页
- 测试友好:Saga 函数返回纯对象序列(Effect),可逐条断言 yield 结果,无需 mock 时间或网络
一个典型可取消的登录 Saga 示例
下面是一个带加载、错误、取消逻辑的登录流程:
function* loginFlow() {
while (true) {
const { payload } = yield take('LOGIN_REQUEST');
const task = yield fork(authorize, payload);
const action = yield take(['LOGIN_SUCCESS', 'LOGIN_FAILURE', 'LOGOUT']);
if (action.type !== 'LOGIN_SUCCESS' && action.type !== 'LOGIN_FAILURE') {
yield cancel(task); // 收到 LOGOUT,取消登录任务
}
}
}
<p>function* authorize({ username, password }) {
try {
const token = yield call(api.login, { username, password });
yield put({ type: 'LOGIN_SUCCESS', token });
} catch (err) {
yield put({ type: 'LOGIN_FAILURE', error: err.message });
}
}</p>关键要点与注意事项
- Saga 必须注册到 store(通过 applyMiddleware(createSagaMiddleware())),否则 yield 不会被拦截执行
- 所有异步副作用必须用 call / cps / fork 等 Effect 包裹,直接写 fetch() 不会暂停,也无法被取消
- 避免在 Saga 中直接修改 state 或调用 this.setState;所有状态变更应通过 put 发送 action
- 注意内存泄漏:fork 启动的子任务若未正确 cancel 或 done,可能持续监听或轮询
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











