调度 StopAction 不会取消任务。即使调用了 StopAction,successAction 或 errorAction 仍会被调度。
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);
}
}
答案 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
尽管对于更复杂的实现(可能像这个),您最终还是会使用自定义实现。