在不丢失数据的情况下发送请求并保持时间顺序
Sending requests without losing data & keep chronological order
我有一个使用 redux saga 和 axios 发送的 reactJS webapp
selecting 产品的网络请求。
它目前使用 takeEvery 作为 saga。
问题:
- 选择产品:如果用户 select 产品非常快(通过单击复选框),则缺少请求数据。例如:用户点击产品:a、b、c、d、e、f 但 b 和 c 不是 selected.
- 切换:我尝试使用 takeLatest 但如果用户 select & deselect 产品非常快,则存在竞争条件 /订单未保留。
举个例子:用户真快selects:产品a,取消了,selects了,取消了,selects了
最终状态是 'cancel' 尽管最后一个动作是 selecting 它。
它应该如何工作:
选择产品:如果用户select的产品非常快,则应正确添加所有产品。
示例:用户添加 a、b、c、d、e、f、g、h、i,因此应添加所有这些。
切换:如果用户重复快速切换某些内容,最终状态必须与最新操作相匹配
例如:用户 selects,取消,selects,取消 => 最终状态是取消
此外:用户 selects => 取消 => selects => 最终状态 id select
实现这个的适当技术方法是什么?
您可能会在不知不觉中打开一堆蠕虫,这取决于您是多么的完美主义者以及您需要您的应用程序有多可靠。如果你想要乐观的更新,天哪。
事情会变得更加困难,因为 服务器也没有响应。
您的数据库也一样,可以 run into concurrency issues and so you might want to start versioning your data。
您可以使用一些策略来减少此类问题。
以无状态方式编写端点或最小化发送状态的数量。例如,不发送所有选定项目的列表,只发送应切换的单个项目。
利用 throttle & debounce saga 效果,它们将帮助您处理请求延迟、跳过和分发,以最大限度地减少出现问题的可能性。
考虑阻止用户操作,直到您收到有意义的回复
请注意,取消 saga(例如通过 takeLatest
效果)不会取消 ajax 请求本身(无论如何它仍会到达您的 BE ).它只会取消 saga 本身(换句话说,它会阻止你的应用程序在你从 BE 收到数据后 运行 代码)
另请注意,使用 takeLatest
取消 sagas 是有限制的。例如。如果你只有一个实体,你可能总是想取消之前的 saga,因为之前响应的数据不再相关。
yield takeLatest(DO_ACTION, actionSaga);
但是,如果您在多个实体之上执行相同的操作,并且您只想在使用同一实体而不是另一个实体时取消之前的操作,该怎么办?如果实体列表是静态的,则可以改用函数模式:
yield takeLatest(action => action.type === DO_ACTION && action.id = 'foo', actionSaga);
yield takeLatest(action => action.type === DO_ACTION && action.id = 'bar', actionSaga);
yield takeLatest(action => action.type === DO_ACTION && action.id = 'baz', actionSaga);
一旦列表变得太长或者如果您有动态 ids/amount 个实体,您将需要使用无限循环编写自己的逻辑,id:task 映射和取消效果。
export function* dynamicIdWatcher() {
const taskMap = {}
while (true) {
let action = yield take(DO_ACTION)
if (taskMap[action.id]) yield cancel(taskMap[action.id])
const workerTask = yield fork(actionSaga, action)
taskMap[action.id] = workerTask
}
}
很抱歉没有真正回答您的问题,但我希望我写的一些内容仍然可以帮助您了解用例:)
我有一个使用 redux saga 和 axios 发送的 reactJS webapp selecting 产品的网络请求。
它目前使用 takeEvery 作为 saga。
问题:
- 选择产品:如果用户 select 产品非常快(通过单击复选框),则缺少请求数据。例如:用户点击产品:a、b、c、d、e、f 但 b 和 c 不是 selected.
- 切换:我尝试使用 takeLatest 但如果用户 select & deselect 产品非常快,则存在竞争条件 /订单未保留。
举个例子:用户真快selects:产品a,取消了,selects了,取消了,selects了 最终状态是 'cancel' 尽管最后一个动作是 selecting 它。
它应该如何工作:
选择产品:如果用户select的产品非常快,则应正确添加所有产品。 示例:用户添加 a、b、c、d、e、f、g、h、i,因此应添加所有这些。
切换:如果用户重复快速切换某些内容,最终状态必须与最新操作相匹配
例如:用户 selects,取消,selects,取消 => 最终状态是取消 此外:用户 selects => 取消 => selects => 最终状态 id select
实现这个的适当技术方法是什么?
您可能会在不知不觉中打开一堆蠕虫,这取决于您是多么的完美主义者以及您需要您的应用程序有多可靠。如果你想要乐观的更新,天哪。
事情会变得更加困难,因为 服务器也没有响应。
您的数据库也一样,可以 run into concurrency issues and so you might want to start versioning your data。
您可以使用一些策略来减少此类问题。
以无状态方式编写端点或最小化发送状态的数量。例如,不发送所有选定项目的列表,只发送应切换的单个项目。
利用 throttle & debounce saga 效果,它们将帮助您处理请求延迟、跳过和分发,以最大限度地减少出现问题的可能性。
考虑阻止用户操作,直到您收到有意义的回复
请注意,取消 saga(例如通过
takeLatest
效果)不会取消 ajax 请求本身(无论如何它仍会到达您的 BE ).它只会取消 saga 本身(换句话说,它会阻止你的应用程序在你从 BE 收到数据后 运行 代码)另请注意,使用
takeLatest
取消 sagas 是有限制的。例如。如果你只有一个实体,你可能总是想取消之前的 saga,因为之前响应的数据不再相关。
yield takeLatest(DO_ACTION, actionSaga);
但是,如果您在多个实体之上执行相同的操作,并且您只想在使用同一实体而不是另一个实体时取消之前的操作,该怎么办?如果实体列表是静态的,则可以改用函数模式:
yield takeLatest(action => action.type === DO_ACTION && action.id = 'foo', actionSaga);
yield takeLatest(action => action.type === DO_ACTION && action.id = 'bar', actionSaga);
yield takeLatest(action => action.type === DO_ACTION && action.id = 'baz', actionSaga);
一旦列表变得太长或者如果您有动态 ids/amount 个实体,您将需要使用无限循环编写自己的逻辑,id:task 映射和取消效果。
export function* dynamicIdWatcher() {
const taskMap = {}
while (true) {
let action = yield take(DO_ACTION)
if (taskMap[action.id]) yield cancel(taskMap[action.id])
const workerTask = yield fork(actionSaga, action)
taskMap[action.id] = workerTask
}
}
很抱歉没有真正回答您的问题,但我希望我写的一些内容仍然可以帮助您了解用例:)