Redux saga - 全有或全无取消 - 产生了正确的方法吗?

时间:2016-12-05 10:20:29

标签: javascript redux spawn redux-saga

在我的应用程序中,我有一个切换,当其值发生变化时,会向服务器发送一些数据。 为确保数据不会发送太多次,我将请求debounce 300毫秒。这是一个例子:

function* watchChangeAction() {
    let task;
    while (true) {
        const action = yield take(actionTypes.CHANGE);
        if (task) {
            yield cancel(task);
        }
        task = yield fork(updateDataInServer, action);
    }

}

function* updateDataInServer(action) {
    try {
        // debounce 300 ms
        yield call(delay, 300);

       //retrieve key from the store
        const key = yield select(selectors.getKey);
        const newSettings = yield select(selectors.getSettingsItems);

        const response = yield call(serverSettings.update, newSettings, key);
        const responseKey = serverSelectors.getKey(response);
       // dispatch action with responseKey, which is stored for later use
        yield put(actions.saved(responseKey));

    } 
//...
 }

问题是我的服务器在每个响应中返回一个不同的密钥。为了能够调用服务器,我必须在我的请求中提供最新的密钥,我在myStore中维护它(参见上面的const key = yield select(selectors.getKey))。

在请求发出后但在收到服务器响应之前完成取消时,会出现问题。 发生的事情是请求已经消失(因此已经提供了新密钥),但是当响应到达时,任务已经被取消,因此响应密钥永远不会被存储,并且不可能发送任何更多请求。

在调查此问题后,我决定选择spawn以确保采用全有或全无的方法。如果任务在延迟之前被取消(对请求的预防),那么它将被忽略,但是,如果请求已经发生,则整个过程将发生。所以updateDataInServer现在看起来像这样:

 try {
      // debounce 300 ms
        yield call(delay, 300);

        const key = yield select(selectors.getKey);
        const newSettings = yield select(selectors.getSettingsItems);

         yield spawn(function*() {
               const response = yield call(serverSettings.update, newSettings, key);
               const responseKey = serverSelectors.getKey(response);
               yield put(actions.saved(responseKey));
        });

    } 
//...

这是正确的做法吗? 我只是为了获得spawn而从版本0.95升级到0.13.0,因为我找不到另一种方法来解决这个问题。可以使用旧版redux-saga来实现我想要的行为吗?

**编辑**

这种方法实际上也不起作用。如果第二次呼叫是在300毫秒之后和响应之前进行的,那么我面临着向服务器发送错误密钥的相同情况。我找到了解决它的方法,这非常麻烦和不洁(如果有人有兴趣会分享),但我想知道 - 这种全有或无的惯用方法是什么? redux-saga中的任务?

1 个答案:

答案 0 :(得分:0)

如果我理解正确,你有几个要求:

  • updateDataInServer的调用应按顺序进行,以便下次调用能够使用上次调用中的令牌
  • 应限制对updateDataInServer的调用。如果对updateDataInServer的呼叫结束并且用户多次再次触发呼叫,则只有最后一次呼叫应该继续

我认为redux-saga对于这种情况来说是正确的。它提供了一种称为actionChannel的东西。 actionChannel充当缓冲区,允许您按顺序处理操作。默认情况下,这意味着最终将处理所有操作,但通过使用自定义缓冲区配置它,您可以控制进入的操作会发生什么。

在下面的示例中,我使用1的滑动缓冲区配置了频道。这意味着如果您的传奇未准备好处理下一个操作(它仍然忙于调用updateDataInServer),它将排队一个操作。每当有新动作进入时,它都会从队列中阻止当前操作并将其替换为新操作。

import { buffers } from 'redux-saga'
import { actionChannel } from 'redux-saga/effects'

function* watchChangeAction() {
  const requestChannel = yield actionChannel((actionTypes.CHANGE, buffers.sliding(1));
  while (true) {
    const action = yield take(requestChannel;
    task = yield call(updateDataInServer, action);
  }
}

You can read more about channels here

And here's some more info about types of buffers