Redux-saga使用takeEvery时如何在派发logout时取消运行中saga
实现思路
两种写法的问题非常明确:
- 初始while循环写法的核心缺陷:执行到
yield take(actionTypes.auth.LOGOUT_USER)时会完全阻塞,等待logout动作派发,根本不会回到循环头部继续监听后续的RUN_COURSES_SAGA动作,因此只有第一次触发目标动作时能启动saga;同时该写法仅保存了单个任务引用,就算解决阻塞问题,多次触发动作产生的并行任务也无法被全部追踪到,取消时会遗漏。 - 直接使用
takeEvery的问题:takeEvery执行后会返回它创建的监听器任务对象,没有持有这个对象引用,自然没办法在logout时触发取消。
核心原理:redux-saga 内置的
takeEvery/takeLatest/takeLeading等监听器effect,返回的watcher任务被取消时,会自动级联取消该watcher下所有正在运行的worker任务,不需要手动维护单个任务的引用列表,这是框架内置的默认行为。
最简实现方案
直接持有takeEvery返回的监听器引用,在logout时取消即可,外层加循环保证取消监听器后,后续触发目标动作仍能正常响应:
export default function* rootSaga() { while (true) { // 启动目标动作监听器,每次匹配到动作自动fork执行处理saga const coursesSagaWatcher = yield takeEvery( actionTypes.sagas.RUN_COURSES_SAGA, processCourseLibraryFiles ) // 阻塞等待logout动作 yield take(actionTypes.auth.LOGOUT_USER) // 取消监听器,自动终止该监听器下所有正在运行的处理任务 yield cancel(coursesSagaWatcher) } }
自定义可控方案(按需选用)
如果需要对单个任务做更细粒度的控制,也可以自行维护运行中任务的集合,实现逻辑如下:
export default function* rootSaga() { const runningCoursesTasks = new Set() while (true) { // 同时监听两个动作,谁先触发就先处理谁 const receivedAction = yield take([ actionTypes.sagas.RUN_COURSES_SAGA, actionTypes.auth.LOGOUT_USER ]) // 触发logout时取消所有运行中的任务,清空集合 if (receivedAction.type === actionTypes.auth.LOGOUT_USER) { for (const task of runningCoursesTasks) { yield cancel(task) } runningCoursesTasks.clear() continue } // 匹配到目标动作时启动处理任务,加入运行集合 const processTask = yield fork(processCourseLibraryFiles) runningCoursesTasks.add(processTask) // 任务执行结束(成功/失败/被取消)后自动从集合移除引用,避免内存泄漏 yield fork(function* () { yield join(processTask) runningCoursesTasks.delete(processTask) }) } }
内容的提问来源于stack exchange,提问作者Andrew Fredette
相关产品推荐
相关产品推荐

