Redux-Saga中如何实现多个阻塞saga按顺序串行执行
核心原因
你的代码无法走到第二个saga的根本原因是:takeEvery创建的是常驻的事件监听器,启动后会持续监听匹配的action,永远不会自动结束执行。因此yield call(spy1)会一直阻塞在spy1的执行流程里,永远不会执行到下一行的yield call(spy2)。
你之前测试的fork属于无阻塞调用,启动spy1后不会等待它执行完成,会立刻往下执行启动spy2,最终效果是两个监听器并行运行,不符合你顺序执行的要求。
实现顺序执行的方法
要实现「前一个saga完全执行完毕再启动下一个」,你首先需要明确定义前一个saga的结束条件——常驻监听器本身没有自然结束的时机,必须由你主动触发终止,才能让call的阻塞解除,流程往下走。
场景1:前一个saga是长期监听,收到明确结束信号后才终止
如果你的spy1需要持续监听action、处理多轮业务,直到收到阶段完成的信号再退出,可以参考下面的写法:
import { takeEvery, call, take, cancel } from 'redux-saga/effects'; function* logger() { console.log('spy 1'); } function* logger2() { console.log('spy2'); } function* spy1() { // 启动监听,保存监听器任务实例 const watcher = yield takeEvery('*', logger); // 阻塞等待代表第一阶段结束的action,比如业务全流程走完时派发的'SPY1_STAGE_COMPLETE' yield take('SPY1_STAGE_COMPLETE'); // 取消常驻监听器,spy1执行完毕 yield cancel(watcher); } function* spy2() { const watcher = yield takeEvery('*', logger2); yield take('SPY2_STAGE_COMPLETE'); yield cancel(watcher); } export default function* rootSaga() { // call会阻塞等待spy1完全返回后,才会执行下一行启动spy2 yield call(spy1); yield call(spy2); }
场景2:前一个saga只需要处理单次任务,不需要长期监听
如果你的spy1不需要持续监听,只需要处理单次action、跑完单次业务逻辑就结束,完全不需要用takeEvery,直接用take做单次监听即可,saga跑完逻辑会自然结束,不需要手动取消:
function* spy1() { // 等待第一次匹配的action const action = yield take('TRIGGER_SPY1'); // 执行业务逻辑 console.log('spy1 处理任务', action); // 函数执行到末尾自然退出,call阻塞解除 } function* spy2() { const action = yield take('TRIGGER_SPY2'); console.log('spy2 处理任务', action); } export default function* rootSaga() { yield call(spy1); yield call(spy2); }
注意事项
- 所有基于
takeEvery/takeLatest/takeLeading创建的监听器,默认生命周期和根saga一致,只要不手动取消就会一直运行,不要期待它会自动结束让阻塞的call往下走 - 判定saga执行阶段结束一定要用明确的信号(比如阶段完成的action、所有子任务执行完成的状态),不要用固定延时这类不可靠的方式判断,否则很容易出现顺序错乱的问题
内容的提问来源于stack exchange,提问作者Jaya DeHart
相关产品推荐
相关产品推荐

