使用redux-saga调度操作时取消传奇

时间:2016-06-07 21:20:17

标签: javascript redux 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操作时,我无法停止传奇。我尝试在我的工作者传奇中使用cancelcancelled效果:

if(yield(take(STOP)) {
  yield cancel(timerTickWorkerSaga)
}

以及第一个代码块中的方法,我尝试从观看服务中停止传奇。

3 个答案:

答案 0 :(得分:11)

看起来这里发生了一些事情:

  1. cancel副作用takes a Task object as its argument。您在上面的代码中传递给它的只是创建saga / Generator对象的GeneratorFunction。有关生成器及其工作原理的详细介绍,请查看this article
  2. 您在yield*takeEvery生成器之前使用takeLatest。使用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效果,因此它不会阻止下一行。

  3. 您传递给takeLatest的第二个参数只是一个对象 - CANCEL effect objecttakeLatest的第二个参数实际上应该是GeneratorFunction,当匹配STOP模式的操作被调度到Redux存储时,它将运行。所以这应该是一个传奇功能。您希望此操作取消fork(takeEvery, [START, TICK], timerTickWorkerSaga)任务,以便将来的STARTTICK操作不会导致timerTickWorkerSaga运行。您可以通过让saga与CANCEL效果产生的Task对象产生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) } 。因此,我们最终会得到以下内容:

    main
  4. 有关其他参考,请查看redux-saga文档中的additional argument。如果你查看那里的fork传奇,你会看到Task效果如何产生一个cancel对象/描述符,当你产生my $delimiter = "\t"; my $line = "zyz pqr abc xyz"; my $count = () = $line =~ /$delimiter/g; # $count is now 3 print $count; 时会被进一步使用效果。

答案 1 :(得分:5)

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);
}

答案 2 :(得分:4)

Redux-Saga现在有一种方法,它被称为种族race。它将运行2个任务,但当一个任务完成时,它将自动取消另一个任务。

  • https://redux-saga.js.org/docs/advanced/RacingEffects.html

  • watchStartTickBackgroundSaga一直在运行

  • 每当有一个开始或勾选时,在timerTickWorkerSaga和听取下一个STOP动作之间开始比赛。
  • 当其中一个任务完成时,其他任务将被取消 这是种族的行为。
  • 姓名"任务"和"取消"在种族内部无所谓,他们只是帮助代码的可读性
export function* watchStartTickBackgroundSaga() {
  yield takeEvery([START, TICK], function* (...args) {
    yield race({
      task: call(timerTickWorkerSaga, ...args),
      cancel: take(STOP)
    })
  })
}