takeEvery监听未触发取消回调,如何检测watchers的取消状态?
关于Redux Saga Watcher取消的问题解答
你的观察是否正确?
完全正确!当你取消allMyTasks这个包含多个takeEvery watcher的组合任务后,这些watcher确实会停止响应新的FOO动作。
原因在于takeEvery的工作机制:它内部会启动一个持续监听指定action的循环,每次匹配到action就fork一个worker saga执行。当你取消watcher所在的任务时,这个监听循环会被终止,watcher不再执行take effect去捕获新的action,自然也就不会再触发后续的worker逻辑。
如何检测watcher的取消状态?
要检测watcher是否被取消,以及让finally块正确触发,你需要在每个watcher saga内部添加try/finally逻辑,并结合Redux Saga提供的cancelled() effect来判断退出原因。具体做法如下:
1. 在watcher中包裹try/finally块
function* watchFoo() { try { // 正常启动takeEvery监听 yield takeEvery('FOO', handleFooWorker); } finally { // 检查是否是因为取消导致的watcher终止 if (yield cancelled()) { console.log('watchFoo 已被取消,停止监听FOO动作'); // 这里可以添加取消后的清理逻辑,比如关闭订阅、释放资源等 } } } function* watchBar() { try { yield takeEvery('BAR', handleBarWorker); } finally { if (yield cancelled()) { console.log('watchBar 已被取消'); } } }
2. 正确管理组合任务的取消
在根saga中,你需要通过fork来启动这些watcher的组合任务,这样才能在登出时正确取消:
function* rootSaga() { // 用fork启动所有watcher的组合任务 const allWatchersTask = yield fork(function*() { yield all([watchFoo(), watchBar()]); }); // 监听登出动作 yield take('LOGOUT'); // 取消所有watcher任务 yield cancel(allWatchersTask); }
这样,当allWatchersTask被取消时,每个watcher的finally块都会被触发,你可以在其中处理取消后的逻辑。
为什么你的finally块没有触发?
大概率是因为你没有把finally块放在watcher saga的try代码块内部,而是放在了worker saga或者外层任务中:
- 如果
finally是在worker saga里:takeEvery每次触发action都会fork新的worker,取消watcher不会终止已经在运行的worker(除非你手动取消),但worker完成后finally会执行;但watcher本身的终止不会触发worker的finally。 - 如果
finally是在外层组合任务中:如果是用all直接阻塞执行而不是fork,那么all会等待所有watcher完成才会执行后续逻辑,而watcher是持续运行的,所以外层的finally永远不会触发。
只有把finally放在每个watcher自己的try块中,才能在watcher被取消时触发对应的逻辑。
内容的提问来源于stack exchange,提问作者Noitidart
相关产品推荐
相关产品推荐

