Redux Saga 运行中任务无法正常取消问题求助
Redux Saga 任务取消失败问题解决方案
问题根因
你现有代码的核心错误出在取消逻辑的目标指向完全错误:
cancelOverviewSaga中通过yield fork(fetchOverviewSaga)生成的是全新的saga任务实例,和之前触发startOverview时takeLatest启动的正在运行的fetchOverviewSaga没有任何关联- 你实际取消的是刚创建的、还没执行业务逻辑的新任务,这也是为什么你会立刻看到
saga task canceled日志,而旧的运行中任务完全没收到取消信号,会正常跑完所有流程
修复方案
方案1:手动维护运行中任务引用
该方案可以同时保留原本takeLatest自动取消上一次同类型任务的特性,适配大多数业务场景:
import { Task } from 'redux-saga'; // 引入Task类型做TS类型标注 function* fetchOverviewSaga(): SagaReturnType<any> { try { yield delay(5000); console.log('still running'); const response = yield call(getOverviewData); console.log('still running'); yield all([ put(updateTagsPendingState(false)), put(updateTagsDataState(response.items)), ]); } finally { if(yield cancelled()) { console.log('saga task canceled'); } } } function* overviewSaga() { // 存储当前正在运行的fetch任务引用 let runningFetchTask: Task | null = null; while (true) { // 同时监听启动和取消两个动作 const action = yield take([startOverview, cancelOverview]); if (action.type === startOverview.type) { // 存在正在运行的任务时先取消上一个,实现takeLatest原有效果 if (runningFetchTask) { yield cancel(runningFetchTask); } // 启动新任务并存储引用 runningFetchTask = yield fork(fetchOverviewSaga); } if (action.type === cancelOverview.type) { // 触发取消时取消当前运行中的任务 if (runningFetchTask) { yield cancel(runningFetchTask); runningFetchTask = null; } } } }
方案2:使用race效果(内聚取消逻辑)
如果不想额外维护任务变量,也可以直接在业务saga内监听取消动作,逻辑更内聚:
function* fetchOverviewSaga(): SagaReturnType<any> { try { // race会在其中一个分支完成后自动取消其他分支的任务 const { cancel } = yield race({ business: call(function*() { yield delay(5000); console.log('still running'); const response = yield call(getOverviewData); console.log('still running'); yield all([ put(updateTagsPendingState(false)), put(updateTagsDataState(response.items)), ]); }), // 等待cancelOverview动作触发 cancel: take(cancelOverview) }); if (cancel) { console.log('saga task canceled'); } } finally { if(yield cancelled()) { console.log('saga task canceled'); } } } // 根saga保持原来的takeLatest即可 function* overviewSaga() { yield takeLatest(startOverview, fetchOverviewSaga); }
效果验证
修复后按照你的复现步骤操作:
- 触发
startOverviewaction - 立即触发取消操作
- 正在等待的
delay(5000)会被立刻中断,直接进入fetchOverviewSaga的取消逻辑,打印取消日志 - 不会出现5秒等待,也不会执行后续的
console.log和putdispatch操作
内容的提问来源于stack exchange,提问作者RGLSV
相关产品推荐
相关产品推荐

