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现在有了一个方法,它被称为race
    race
    。它将运行两个任务,但当一个任务完成时,它将自动取消另一个任务

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