当使用 redux-saga 发送一个动作时取消一个 saga
Cancel a saga when an action is dispatched with redux-saga
我在调度 START 操作时为秒表 React 组件启动计时器:
import 'babel-polyfill'
import { call, put } from 'redux-saga/effects'
import { delay, takeEvery, takeLatest } from 'redux-saga'
import { tick, START, TICK, STOP } from './actions'
const ONE_SECOND = 1000
export function * timerTickWorkerSaga (getState) {
yield call(delay, ONE_SECOND)
yield put(tick())
}
export default function * timerTickSaga () {
yield* takeEvery([START, TICK], timerTickWorkerSaga)
yield* takeLatest(STOP, cancel(timerTickWorkerSaga))
}
/*
The saga should start when either a START or a TICK is dispatched
The saga should stop running when a stop is dispatched
*/
当从我的组件调度 STOP
操作时,我无法停止 saga。我尝试在我的 worker saga 中使用 cancel
和 cancelled
效果:
if(yield(take(STOP)) {
yield cancel(timerTickWorkerSaga)
}
以及第一个代码块中的方法,我尝试从监视服务中停止 saga。
看起来这里发生了一些事情:
cancel
副作用 takes a Task
object as its argument. What you're passing into it in the code above is just the GeneratorFunction
that creates the saga/Generator object. For a great intro to generators and how they work, check out this article。
您在 takeEvery
和 takeLatest
生成器之前使用 yield*
。使用 yield*
将 spread the whole sequence。所以你可以这样想:它正在填充行
yield* takeEvery([START, TICK], timerTickWorkerSaga)
和
while (true) {
const action = yield take([START, TICK])
yield fork(timeTickWorkerSaga, action)
}
而且我认为这不是您想要的,因为我相信这最终会阻塞您 timerTickSaga
的第二行。相反,您可能想要:
yield fork(takeEvery, [START, TICK], timerTickWorkerSaga)
这分叉了 takeEvery
效果,因此它不会阻塞下一行。
您传递给 takeLatest
的第二个参数只是一个对象 - CANCEL effect object. The second argument to takeLatest
should actually be a GeneratorFunction
, which will be run when an action matching the STOP
pattern is dispatched to the Redux store. So that should really be a saga function. You want this to cancel the fork(takeEvery, [START, TICK], timerTickWorkerSaga)
task so that future START
and TICK
actions will not cause the timerTickWorkerSaga
to run. You can achieve this by having the saga run a CANCEL
effect with the Task
object that resulted from the fork(takeEvery...
effect. We can the Task
object as an additional argument 到 takeLatest
传奇。所以我们最终得到了一些类似的东西:
export default function * timerTickSaga () {
const workerTask = yield fork(takeEvery, [START, TICK], timerTickWorkerSaga)
yield fork(takeLatest, STOP, cancelWorkerSaga, workerTask)
}
function* cancelWorkerSaga (task) {
yield cancel(task)
}
如需更多参考,请查看 redux-saga 文档中的 task cancellation example。如果你查看那里的 main
传奇,你会看到 fork
效果如何产生 Task
object/descriptor,在产生 cancel
时进一步使用它效果。
rayd 的回答非常正确,但在内部 takeEvery 和 takeLatest 进行分叉的方式上有点多余。
可以看解释 here:
所以代码应该是:
export default function* timerTickSaga() {
const workerTask = yield takeEvery([START, TICK], timerTickWorkerSaga);
yield takeLatest(STOP, cancelWorkerSaga, workerTask);
}
function* cancelWorkerSaga(task) {
yield cancel(task);
}
Redux-Saga 现在有一个方法,叫做 race race
。它将 运行 2 个任务,但是当一个完成时,它会自动取消另一个。
watchStartTickBackgroundSaga 总是 运行ning
- 每次开始或滴答时,开始 timerTickWorkerSaga 之间的竞赛并监听下一个 STOP 动作。
- 当其中一个任务完成时,另一个任务被取消
这是种族的行为。
- race 中的名字 "task" 和 "cancel" 无关紧要,它们只是有助于代码的可读性
export function* watchStartTickBackgroundSaga() {
yield takeEvery([START, TICK], function* (...args) {
yield race({
task: call(timerTickWorkerSaga, ...args),
cancel: take(STOP)
})
})
}
我在调度 START 操作时为秒表 React 组件启动计时器:
import 'babel-polyfill'
import { call, put } from 'redux-saga/effects'
import { delay, takeEvery, takeLatest } from 'redux-saga'
import { tick, START, TICK, STOP } from './actions'
const ONE_SECOND = 1000
export function * timerTickWorkerSaga (getState) {
yield call(delay, ONE_SECOND)
yield put(tick())
}
export default function * timerTickSaga () {
yield* takeEvery([START, TICK], timerTickWorkerSaga)
yield* takeLatest(STOP, cancel(timerTickWorkerSaga))
}
/*
The saga should start when either a START or a TICK is dispatched
The saga should stop running when a stop is dispatched
*/
当从我的组件调度 STOP
操作时,我无法停止 saga。我尝试在我的 worker saga 中使用 cancel
和 cancelled
效果:
if(yield(take(STOP)) {
yield cancel(timerTickWorkerSaga)
}
以及第一个代码块中的方法,我尝试从监视服务中停止 saga。
看起来这里发生了一些事情:
cancel
副作用 takes aTask
object as its argument. What you're passing into it in the code above is just theGeneratorFunction
that creates the saga/Generator object. For a great intro to generators and how they work, check out this article。您在
takeEvery
和takeLatest
生成器之前使用yield*
。使用yield*
将 spread the whole sequence。所以你可以这样想:它正在填充行yield* takeEvery([START, TICK], timerTickWorkerSaga)
和
while (true) { const action = yield take([START, TICK]) yield fork(timeTickWorkerSaga, action) }
而且我认为这不是您想要的,因为我相信这最终会阻塞您
timerTickSaga
的第二行。相反,您可能想要:yield fork(takeEvery, [START, TICK], timerTickWorkerSaga)
这分叉了
takeEvery
效果,因此它不会阻塞下一行。您传递给
takeLatest
的第二个参数只是一个对象 - CANCEL effect object. The second argument totakeLatest
should actually be aGeneratorFunction
, which will be run when an action matching theSTOP
pattern is dispatched to the Redux store. So that should really be a saga function. You want this to cancel thefork(takeEvery, [START, TICK], timerTickWorkerSaga)
task so that futureSTART
andTICK
actions will not cause thetimerTickWorkerSaga
to run. You can achieve this by having the saga run aCANCEL
effect with theTask
object that resulted from thefork(takeEvery...
effect. We can theTask
object as an additional argument 到takeLatest
传奇。所以我们最终得到了一些类似的东西:export default function * timerTickSaga () { const workerTask = yield fork(takeEvery, [START, TICK], timerTickWorkerSaga) yield fork(takeLatest, STOP, cancelWorkerSaga, workerTask) } function* cancelWorkerSaga (task) { yield cancel(task) }
如需更多参考,请查看 redux-saga 文档中的 task cancellation example。如果你查看那里的 main
传奇,你会看到 fork
效果如何产生 Task
object/descriptor,在产生 cancel
时进一步使用它效果。
rayd 的回答非常正确,但在内部 takeEvery 和 takeLatest 进行分叉的方式上有点多余。 可以看解释 here:
所以代码应该是:
export default function* timerTickSaga() {
const workerTask = yield takeEvery([START, TICK], timerTickWorkerSaga);
yield takeLatest(STOP, cancelWorkerSaga, workerTask);
}
function* cancelWorkerSaga(task) {
yield cancel(task);
}
Redux-Saga 现在有一个方法,叫做 race race
。它将 运行 2 个任务,但是当一个完成时,它会自动取消另一个。
watchStartTickBackgroundSaga 总是 运行ning
- 每次开始或滴答时,开始 timerTickWorkerSaga 之间的竞赛并监听下一个 STOP 动作。
- 当其中一个任务完成时,另一个任务被取消 这是种族的行为。
- race 中的名字 "task" 和 "cancel" 无关紧要,它们只是有助于代码的可读性
export function* watchStartTickBackgroundSaga() {
yield takeEvery([START, TICK], function* (...args) {
yield race({
task: call(timerTickWorkerSaga, ...args),
cancel: take(STOP)
})
})
}