21

当一个 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当从我的组件分派操作时,我无法停止传奇。我尝试过在我的工人传奇中使用cancel和效果:cancelled

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

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

4

3 回答 3

14

Redux-Saga 现在有一个方法,叫做 race 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)
    })
  })
}
于 2017-08-21T22:06:10.957 回答
13

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

  1. cancel副作用将对象作为Task其参数。您在上面的代码中传递给它的只是GeneratorFunction创建 saga/Generator 对象的内容。有关生成器及其工作原理的精彩介绍,请查看这篇文章
  2. 您在and生成器yield*之前使用。使用将传播整个序列。所以你可以这样想:它正在填补这条线takeEverytakeLatestyield*

    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 效果对象。的第二个参数takeLatest实际上应该是 a GeneratorFunction,它将在匹配STOP模式的操作被分派到 Redux 存储时运行。所以这应该是一个传奇功能。您希望它取消fork(takeEvery, [START, TICK], timerTickWorkerSaga)任务,以便未来STARTTICK操作不会导致timerTickWorkerSaga运行。您可以通过让 saga 使用效果产生的对象运行CANCEL效果来实现此目的。我们可以将对象作为saga的附加参数。所以我们最终得到了一些类似的东西:Taskfork(takeEvery...TasktakeLatest

    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 文档中的任务取消示例。如果您在main那里查看 saga,您将看到fork效果如何产生一个Task对象/描述符,该对象/描述符在产生效果时会被进一步使用cancel

于 2016-06-08T05:07:17.193 回答
9

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);
}
于 2016-10-30T16:52:34.133 回答