将 redux-saga 与 setInterval 结合使用——如何以及何时产生

Using redux-saga with setInterval - how and when to yield

刚从 thunks 转移到 sagas,我试图找到调用 setTimeout 的最佳方法,然后从该函数内部调用另一个函数(在本例中为 corewar.step())。这是我的原始代码,可以正常工作。

  runner = window.setInterval(() => {

    for(let i = 0; i < processRate; i++) {
      corewar.step()
    }

    operations += processRate;

  }, 1000/60)

此代码位于 saga 内,我相信我应该能够像我在应用程序的其他区域所做的那样将函数调用包装在 call 内。

我试过将 setInterval 调用包装在 call 中,并保留其他所有内容,这导致 step() 永远不会被调用。

  runner = yield call(window.setInterval, () => {

    for(let i = 0; i < processRate; i++) {
      corewar.step()
    }

    operations += processRate;

  }, 1000/60)

我已经尝试过,将 setInterval 保持原样并将 step() 函数包装在一个调用中并将匿名函数签名更改为 function* 这也会导致 step() 从未被调用。

  runner = window.setInterval(function*() {

    for(let i = 0; i < processRate; i++) {
      yield call([corewar, corewar.step])
    }

    operations += processRate;

  }, 1000/60)

最后,我尝试将两者都包装起来,这再次导致 step() 永远不会被调用。

  runner = yield call(window.setInterval, function*() {

    for(let i = 0; i < processRate; i++) {
      yield call([corewar, corewar.step])
    }

    operations += processRate;

  }, 1000/60)

感觉我在这里遗漏了一些东西,所以我的问题是,我是否需要将这些函数包装在 call 中,或者这是错误的吗?

如果我应该将外部 setInterval 包装在 call 中,接下来的问题是我应该如何将函数定义为 call 的参数,这也需要产生 putcall 本身?

const anotherSaga = function * () {
  const runner = yield call(setInterval, () => {
    console.log('yes');
  }, 1000);
  console.log(runner);
}

这对我来说效果很好。在你的第二个片段中,末尾有一个双 ),应该只有一个。

saga-redux 文档中有一个名为“Using the eventChannel factory to connect to external events”的部分,建议使用 channels

本节还提供了 setInterval 实施的示例:

import { eventChannel, END } from 'redux-saga'

function countdown(secs) {
  return eventChannel(emitter => {
      const iv = setInterval(() => {
        secs -= 1
        if (secs > 0) {
          emitter(secs)
        } else {
          // this causes the channel to close
          emitter(END)
        }
      }, 1000);
      // The subscriber must return an unsubscribe function
      return () => {
        clearInterval(iv)
      }
    }
  )
}

然后您将使用 yield callyield takeEvery 进行设置:

const channel = yield call(countdown, 10);
yield takeEvery(channel, function* (secs) {
    // Do your magic..
});

这里的派对有点晚了,但这是在 saga 中设置计时器问题的热门搜索结果。由于 sagas 的性质,有一个替代解决方案。 From here.

我这样改编的:

function* callSelfOnTimer({ value }) {
  // Do your work here
  ...
  // If still true call yourself in 2 seconds
  if (value) {
    yield delay(2000);
    yield call(callSelfOnTimer, { value });
  }
}

要使其正常工作,您还需要添加以下内容:

const delay = (ms) => new Promise(res => setTimeout(res, ms))

function* callSelfOnTimer({ value }) {  
    // Do your work here  
    ...  
    // If still true call yourself in 2 seconds  
    if (value) {  
        yield delay(2000);  
        yield call(callSelfOnTimer, { value });  
    }  
}