Redux Saga - 无法取消任务

Redux Saga - Unable to cancel task

我有一个 React 项目设置为与 redux saga 一起工作,但由于某种原因,我无法取消 运行ning saga / action 任务。预期是,在执行某些操作(例如用户离开或单击按钮)后,运行ning 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 仍然 运行s,只有在它完全完成 [=40] 之后才会在 if(yield cancelled()) 中被捕获=]宁。不确定这是否是实际行为,预计会在请求时取消。欢迎任何想法。

*编辑:

调用取消操作后,fetchOverviewSaga 似乎被取消了,因为它记录了 saga task canceled,但是 运行 剩下的是来自 [=14= 的块], 查看控制台,问题可能出在那个块

*编辑2:

为了更好地说明行为:

当您取消 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 时,您发送了一个动作,该动作被 reducer 捕获或在 saga 中侦听。这就像发送了一封错误的电子邮件,并发送了第二封电子邮件来修复错误一样。因此,您可以在 if (yield cancelled()) 中调度另一个操作来撤消其他操作所做的操作。

一种方式

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

Helpful docs

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

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

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

如果数组中的项目完成,竞争效果会等待一个,然后取消所有其他项目,因此:

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

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