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:
为了更好地说明行为:
- 调度启动概览操作
- 立即取消
- 日志:传奇任务已取消
- 5000 毫秒后(延迟在 fetchOverviewSaga 中结束)
- 日志:'still running' x2 并从 fetchOverviewSaga
调度收益
当您取消 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
在派发取消操作之前完成,它将停止等待取消操作。
我有一个 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:
为了更好地说明行为:
- 调度启动概览操作
- 立即取消
- 日志:传奇任务已取消
- 5000 毫秒后(延迟在 fetchOverviewSaga 中结束)
- 日志:'still running' x2 并从 fetchOverviewSaga 调度收益
当您取消 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
在派发取消操作之前完成,它将停止等待取消操作。