[英]Cancel a saga when an action is dispatched with redux-saga
I start a timer for a stopwatch React component when a START action is dispatched: 在调度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
*/
I have trouble stopping the saga when the STOP
action is dispatched from my component. 从我的组件调度
STOP
动作时,我无法停止传奇。 I have tried using cancel
and cancelled
effects from within my worker saga: 我试过在我的工作者传奇中使用
cancel
和cancelled
效果:
if(yield(take(STOP)) {
yield cancel(timerTickWorkerSaga)
}
as well as the approach in the first code block where I try and stop the saga from the watching service. 以及第一个代码块中的方法,我试图阻止来自观看服务的传奇。
Looks like a few things are going on here: 看起来这里发生了一些事情:
cancel
side effect takes a Task
object as its argument . cancel
副作用将Task
对象作为其参数 。 What you're passing into it in the code above is just the GeneratorFunction
that creates the saga/Generator object. GeneratorFunction
saga / Generator对象的GeneratorFunction。 For a great intro to generators and how they work, check out this article . You're using yield*
before the takeEvery
and takeLatest
generators. 你在
takeEvery
之前使用yield*
并使用takeLatest
生成器。 Using yield*
will spread the whole sequence . 使用
yield*
将扩展整个序列 。 So you can think of it like this: that it's filling in the line 所以你可以这样想:它填补了这条线
yield* takeEvery([START, TICK], timerTickWorkerSaga)
with 同
while (true) { const action = yield take([START, TICK]) yield fork(timeTickWorkerSaga, action) }
And I don't think this is what you're going for, because I believe this will end up blocking the second line of your timerTickSaga
. 而且我认为这不是你想要的,因为我相信这最终会阻止你的
timerTickSaga
的第二行。 Instead you probably want: 相反,你可能想要:
yield fork(takeEvery, [START, TICK], timerTickWorkerSaga)
This forks off the takeEvery
effect so it doesn't block the next line. 这会取消
takeEvery
效果,因此不会阻止下一行。
The second argument you're passing into takeLatest
is just an object - a CANCEL effect object . 你传递给
takeLatest
的第二个参数只是一个对象 - 一个CANCEL效果对象 。 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. takeLatest
的第二个参数实际上应该是一个GeneratorFunction
,它将在匹配STOP
模式的动作被调度到Redux存储时运行。 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. 您希望这取消
fork(takeEvery, [START, TICK], timerTickWorkerSaga)
任务,以便将来的START
和TICK
操作不会导致timerTickWorkerSaga
运行。 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 to the takeLatest
saga. So we end up with something along the lines of: 你可以通过使用由
fork(takeEvery...
产生的Task
对象运行一个CANCEL
效果来实现这一点fork(takeEvery...
效果。我们可以将Task
对象作为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) }
For additional reference check out the task cancellation example in the redux-saga docs. 有关其他参考,请查看redux-saga文档中的任务取消示例 。 If you look in the
main
saga there, you'll see how the fork
effect yields a Task
object/descriptor that is used further down when yielding the cancel
effect. 如果你查看那里的
main
传奇,你会看到fork
效果如何产生一个Task
对象/描述符,在产生cancel
效果时会进一步使用它。
The answer from rayd is very correct but a bit superfluous in the way that takeEvery and takeLatest internally are doing a fork. rayd的答案是非常正确的,但在takeEvery和takeLatest内部正在做分叉的方式有点多余。 You can see the explanation here :
你可以在这里看到解释:
So the code should be: 所以代码应该是:
export default function* timerTickSaga() {
const workerTask = yield takeEvery([START, TICK], timerTickWorkerSaga);
yield takeLatest(STOP, cancelWorkerSaga, workerTask);
}
function* cancelWorkerSaga(task) {
yield cancel(task);
}
Redux-Saga has a method for this now, it's called race race
. Redux-Saga现在有一种方法,它叫做种族
race
。 It will run 2 tasks, but when one finishes, it will automatically cancel the other. 它将运行2个任务,但是当一个任务完成时,它将自动取消另一个任务。
https://redux-saga.js.org/docs/advanced/RacingEffects.html https://redux-saga.js.org/docs/advanced/RacingEffects.html
watchStartTickBackgroundSaga is always running watchStartTickBackgroundSaga一直在运行
export function* watchStartTickBackgroundSaga() {
yield takeEvery([START, TICK], function* (...args) {
yield race({
task: call(timerTickWorkerSaga, ...args),
cancel: take(STOP)
})
})
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.