【问题标题】:Redux Saga - Unable to cancel taskRedux Saga - 无法取消任务
【发布时间】:2021-12-11 12:25:40
【问题描述】:

我有一个设置为使用 redux saga 的 react 项目,但由于某种原因,我无法取消正在运行的 saga / action 任务。期望的是,在某些操作之后(例如用户导航离开或单击按钮),正在运行的 saga 将被取消。尝试捕捉取消的动作,但在 saga 完全运行后不会发生:

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* cancelOverviewSaga(): SagaReturnType<any> {
  const runningAction = yield fork(fetchOverviewSaga);

  yield cancel(runningAction);
}

function* overviewSaga() {
  yield all([
    takeLatest(startOverview, fetchOverviewSaga),
    takeLatest(cancelOverview, cancelOverviewSaga)
  ]);
}

结果是,即使在调度操作以取消 (cancelOverviewSaga) 之后,fetchOverviewSaga 仍然运行,只有在它完全完成运行后才会在 if(yield cancelled()) 中捕获。不确定这是否是实际行为,预计会在请求时取消。欢迎任何想法。

*编辑:

在调用取消操作时,fetchOverviewSaga 似乎被取消了,因为它确实记录了saga task canceled,但是剩下要运行的是来自yield all([..])) 的块,看着控制台,可能问题出在那个块

*编辑2:

为了更好地说明行为:

  • 调度 startOverview 操作
  • 立即取消
  • 日志:saga 任务已取消
  • 5000 毫秒后(在 fetchOverviewSaga 中延迟结束)
  • 日志:'still running' x2 并从 fetchOverviewSaga 调度 yield all

【问题讨论】:

    标签: reactjs generator redux-saga


    【解决方案1】:

    当您取消一个 saga 时,所有子任务都会取消。示例:

    function* mainTask(){
      const task = yield fork(subTask1) // or call
      yield cancel(task)
    }
    
    function* subTask1(){
      yield fork(subTask2) // or call
    }
    
    function* subTask2(){
      ...
    }
    

    在本例中,由于cancel向下传播,subTask1将通过mainTask取消,subTask2将通过subTask1取消。

    但是,当您 yield put 时,您发送了一个动作,该动作被减速器捕获或在 saga 中侦听。这就像发送一封错误的电子邮件,然后发送第二封电子邮件来修复错误。因此,您可以在 if (yield cancelled()) 内调度另一个操作以撤消其他操作所做的操作。

    一种方式

      } finally {
        if(yield cancelled()) {
          yield all[
           put(cancelUpdateTagsPendingState()),
           put(cancelUpdateTagsDataState()),
          ]
        }
      }
    

    Helpful docs

    【讨论】:

    • 谢谢你的回答,最初我认为一旦你点击取消,整个任务就会取消,并跳转到 yield cancelled() 块;这确实发生了,但以一种奇怪的方式仍在继续执行,但不确定这是否正确(在原始问题中添加了编辑 2)
    【解决方案2】:

    同一个 saga 可以在多个实例中独立运行。所以例如如果你这样做了

    const task1 = yield fork(mySaga);
    const task2 = yield fork(mySaga);
    task1 === task2 // false
    

    它将运行 mySaga 的两个独立实例,每个实例都有自己的任务。如果你取消一个,它不会取消另一个。因此,在 cancelOverviewSaga 中分叉并立即取消您的 fetchOverviewSaga saga 不会影响由于调度 startOverview 操作而正在运行的 saga。

    在这种情况下,您可以改用例如race 效果来实现你的目标:

    function* fetchOverviewSaga() {
      try {
        // your fetching logic ...
      } finally {
        if (yield cancelled()) {
          console.log("saga task canceled");
        }
      }
    }
    
    function* startOverviewSaga() {
      yield race([
        call(fetchOverviewSaga),
        take(cancelOverview),
      ]);
    }
    
    function* overviewSaga() {
      yield takeLatest(startOverview, startOverviewSaga);
    }
    

    如果数组中的项目完成,比赛效果会等待一个,然后它会取消所有其他的,所以:

    • 如果在fetchOverviewSaga 完成之前调度cancelOverview 操作,它将取消fetchOverviewSaga
    • 如果 fetchOverviewSaga 在调度取消操作之前完成,它将停止等待取消操作。

    工作演示: https://codesandbox.io/s/https-stackoverflow-com-questions-69717869-redux-saga-unable-to-cancel-task-vu4b0?file=/src/index.js

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-16
      • 2020-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-06
      • 1970-01-01
      相关资源
      最近更新 更多