Javascript 在使用redux saga调度操作时取消saga
我在调度启动操作时启动秒表反应组件的计时器:Javascript 在使用redux saga调度操作时取消saga,javascript,redux,redux-saga,Javascript,Redux,Redux Saga,我在调度启动操作时启动秒表反应组件的计时器: 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 * ti
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。我曾尝试在我的工人传奇中使用cancel
和cancelled
效果:
if(yield(take(STOP)) {
yield cancel(timerTickWorkerSaga)
}
以及第一个代码块中的方法,我尝试从观看服务中停止这段传奇故事。这里似乎发生了一些事情:
取消
副作用。在上面的代码中传递给它的只是创建saga/Generator对象的GeneratorFunction
。有关发电机及其工作原理的精彩介绍,请访问takeEvery
和takeLatest
生成器之前使用yield*
。使用yield*
will。所以你可以这样想:它在排队
yield*takeEvery([START,TICK],timerTickWorkerSaga)
与
我不认为这是你想要的,因为我相信这会阻止你的timerTickSaga
的第二行。相反,您可能希望:
yield fork(takeEvery, [START, TICK], timerTickWorkerSaga)
这会分叉takeEvery
效果,因此不会阻塞下一行takeLatest
的第二个参数只是一个对象-a。takeLatest
的第二个参数实际上应该是一个GeneratorFunction
,当匹配STOP
模式的操作被发送到Redux存储时,将运行该函数。所以这应该是一个传奇的功能。您希望此操作取消分叉(takeEvery[START,TICK],timerTickWorkerSaga)
任务,以便将来的START
和TICK
操作不会导致timerTickWorkerSaga
运行。您可以通过让saga运行CANCEL
效果和Task
对象来实现这一点,该对象是由fork(takeEvery…
效果产生的。我们可以将Task
对象作为takeLatest
saga的一部分。因此,我们最终得到了以下内容:
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文档中的。如果您查看那里的
主saga,您将看到分叉效果如何产生任务对象/描述符,在产生取消效果时会进一步使用该对象/描述符。rayd的回答非常正确,但在实践中有点多余takeEvery和takeLatest内部执行分叉的方式。
您可以看到以下解释:
因此,代码应该是:
export default function* timerTickSaga() {
const workerTask = yield takeEvery([START, TICK], timerTickWorkerSaga);
yield takeLatest(STOP, cancelWorkerSaga, workerTask);
}
function* cancelWorkerSaga(task) {
yield cancel(task);
}
Redux Saga现在有了一个方法,它被称为racerace
。它将运行两个任务,但当一个任务完成时,它将自动取消另一个任务
- watchStartTickBackgroundSaga总是在运行
- 每次有开始或滴答声时,在timerTickWorkerSaga和倾听下一个停止动作之间开始一场竞赛
- 当其中一项任务完成时,另一项任务将被取消
这是种族的行为
- race内部的“task”和“cancel”名称并不重要,它们只是帮助代码的可读性
正如你可能已经怀疑的那样,我一直忽略了ES6生成器,谢谢你的回答和有用的资源。嗨,科里,你的解决方案是我这个案例中唯一对我有效的解决方案。我有两个问题:1)当我使用@Marc提供的解决方案时,这个故事被取消了,但它不会再听到同样的动作。为什么呢?2) 在您的解决方案中,(…args)参数的用途是什么?它甚至可以在不提供这些参数的情况下工作?…args
将保存函数*
的所有参数,并将它们保存为名为args
的变量中的数组。在call(timerTickWorkerSaga,…args)
中使用它们,然后将所有这些参数传递到timerTickWorkerSaga
。在redux传奇中,争论点是在takeEvery
中捕捉到的动作-请看,Marc的解决方案是不再倾听,因为您实际上取消了takeEvery
效果。我的解决方案嵌套在takeEvery
中,实际的timerTickWorkerSaga
任务被取消,而不是takeEvery
。因此,我的解决方案将在每个start/TICK
上开始一场比赛,并在STOP
上取消,但马克的解决方案将在takeEvery start/TICK
上运行,并在STOP
上取消@CoryDanielson Man…这太棒了。谢谢你抽出时间解释事情。那把小提琴帮助我理解了args
。对于分叉任务,这无疑是最好的选择。
export default function* timerTickSaga() {
const workerTask = yield takeEvery([START, TICK], timerTickWorkerSaga);
yield takeLatest(STOP, cancelWorkerSaga, workerTask);
}
function* cancelWorkerSaga(task) {
yield cancel(task);
}
export function* watchStartTickBackgroundSaga() {
yield takeEvery([START, TICK], function* (...args) {
yield race({
task: call(timerTickWorkerSaga, ...args),
cancel: take(STOP)
})
})
}