Saga 取消去抖动作

时间:2021-05-02 23:47:18

标签: redux-saga saga

调度 StopAction 不会取消任务。即使调用了 StopAction,successActionerrorAction 仍会被调度。

function* myTask(actionCreator, action) {
    try {
        const { cancelTask, response } = yield race({
            response: call(apiPromise, action.meta.id),
            cancelTask: take(StopAction.type)
        });
        if (cancelTask !== undefined) {
            return;
        }
        yield put(actionCreator.makeSuccessAction(response, action.meta));
    } catch (e) {
        yield put(actionCreator.makeErrorAction(e, action.meta));
    }
}

function* mySaga() {
    yield debounceFor(
        myActionCreator.loadAction.type,
        myTask,
        250,
        myActionCreator
    );
}

export function* debounceFor(pattern, saga, ms, ...args) {
    function* delayedSaga(action) {
        yield call(delay, ms);
        yield call(saga, ...args, action);
    }

    let task;
    while (true) {
        const action = yield take(pattern);
        if (task) {
            yield cancel(task);
        }

        task = yield fork(delayedSaga, action);
    }
}

1 个答案:

答案 0 :(得分:2)

我认为问题在于您在“延迟”阶段(这是 debounceFor 实现的一部分)期间调用了停止操作,在该阶段 myTask 传奇尚未开始,因此当停止动作发生时,还没有任何东西在监听它。在那之后,myTask 传奇终于开始了,但是您正在运行 race 效果在停止操作已经被分派之后,因此竞争效果以 api 响应结束。

问题是如何解决这个问题。一种选择是将 debounce/myTask sagas 合并在一起,这样您就可以将延迟/api 阶段都包装到单个任务中,并取消当前的竞争效果。

另一种选择是两次侦听停止操作,一次在延迟阶段,然后在您已经处于的 api 调用阶段。去抖动的实现可能如下所示:

export function* debounceFor(pattern, cancelPattern, saga, ms) {
  function* delayedSaga(action) {
    if (cancelPattern) {
      let {cancelTask} = yield race({
        _: delay(ms),
        cancelTask: take(cancelPattern),
      });
      if (cancelTask) return;
    } else {
      yield delay(ms);
    }
    yield call(saga, action);
  }

  let task;
  while (true) {
    const action = yield take(pattern);
    if (task) {
      yield cancel(task);
    }
    task = yield fork(delayedSaga, action);
  }
}
// ....
yield debounceFor(
  myActionCreator.loadAction.type,
  StopAction.type, // added cancel type
  myTask,
  250,
  myActionCreator
);

我还应该指出,最新的 redux-saga 原生支持 debounce: https://redux-saga.js.org/docs/api/#debouncems-pattern-saga-args

尽管对于更复杂的实现(可能像这个),您最终还是会使用自定义实现。