在我的应用程序中,我有一个切换,当其值发生变化时,会向服务器发送一些数据。
为确保数据不会发送太多次,我将请求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
中的任务?
答案 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);
}
}