在我的反应应用程序中,我有一个Grid。用户可以一次选择多个网格行,然后单击按钮以对选定的网格行进行批量操作。
在服务器端,我有一个脚本,我想为每个选定的行执行(为了使问题变得简单,我将在下面的示例中为每个选定的行调用“jsonplaceholder.typicode.com”)批量操作按钮。在批量操作按钮单击时,我在动作创建器中获得selectedRows,我在其中迭代selectedRows并为每个所选行进行ajax调用。
由于selectedRows可能包含超过1000个项目,并且如果我只是使用forEach循环迭代地进行ajax调用,浏览器页面最终可能会在每个请求被解析之前停止响应。因此,我使用下面的解决方案,以5个批次发送请求,然后等到5个解决。
// Action creator, selectedRows is an array.
function onGridRowsSelection(selectedRows) {
makeBatchCalls(selectedRows,5)
}
async function makeBatchCalls(selectedRows, length) {
let test = arrayIds.reduce((rows, key, index) => (index % length == 0
? rows.push([key])
: rows[rows.length-1].push(key)) && rows, []);
let Batchresults = []; //convert them to two dimensionl arrays of given length [[1,2,3,4,5], [6,7,8,9,10]]
for (calls of test) {
Batchresults.push(await Promise.all(calls.map((call)=>{
fetch(`https://jsonplaceholder.typicode.com/posts/${call}`)
})
));
}
return Promise.all(Batchresults); //wait for all batch calls to finish
}
上面的解决方案运行正常,但有一个问题,
我该如何限制?
React - controlling async calls smartly without any side effect in complex applications
中询问了此处提到的问题的后续问题这个问题是JavaScript, React - sending multiple simultaneous ajax calls
的后续问题答案 0 :(得分:2)
async
模块具有以下功能:async.queue
。首先定义一个任务函数。然后你给它一个任务 - 在你的情况下,一个行数组和你想要它采取的行动。如果正在进行任务,则将运行该任务,或将其添加到队列中。任务完成后,下一个任务将从队列中删除。
更好的是,您可以只为一行定义任务函数,并将队列的并发性设置为5.当用户单击该按钮时,您将向队列中添加大量任务,每个行都选择一行。 5个任务将立即开始运行,其余任务将排队。这可能比你尝试做的更好,因为这样用户可以启动2个任务,然后立即启动另外3个任务,并且它们将并行运行。
请尝试以下代码:
const async = require('async'); // or whatever mechanism you're using for module management.
const queue = async.queue((row, callback) => {
fetch(`https://jsonplaceholder.typicode.com/posts/${call}`)
.then(callback, callback);
}, 5);
function onGridRowsSelection(selectedRows) {
for (let call of selectedRows) {
queue.push(call);
}
}
答案 1 :(得分:1)
这肯定会发生,因为在您的代码中,没有检查批处理请求是否已经运行。您必须在代码中进行一些更改才能正确地适应批处理调用。
第1步:
首先,在您的州中保留一个标志,以查看是否已经有批处理请求正在运行,例如 flagBatchRunning 。在触发请求之前,在 makeBatchCalls 函数中将其设置为true。
现在,一旦Promise.all得到解决并且所有请求都已完成,请再次将其设为false。
在您的动作创建者中,检查此标志是否为假。
function onGridRowsSelection(selectedRows) {
if(!state.flagBatchRunning){
makeBatchCalls(selectedRows,5)
}
}
第2步:
只需保留一个标记就不会对您有所帮助,因为在批量调用运行时,用户很可能再次单击批量操作按钮,而 onGridRowsSelection 将忽略此更新案件。因此,现在您需要保留某种变量来存储这些挂起的批处理请求。
为了满足这一需求,请创建一个数组,例如 pendingRequestsArray 。继续在此阵列中添加所有待处理更新,并在完成上一批处理后,从待处理阵列中选择所有请求并为其进行批量调用。
所以你的功能现在改变了。
// Action creator, selectedRows is an array.
function onGridRowsSelection(selectedRows) {
if(!state.flagBatchRunning){
makeBatchCalls(selectedRows,5)
}else{
state.pendingRequestsArray.push(selectedRows); //push to pending array
}
}
async function makeBatchCalls(selectedRows, length) {
let test = arrayIds.reduce((rows, key, index) => (index % length == 0
? rows.push([key])
: rows[rows.length-1].push(key)) && rows, []);
let Batchresults = []; //convert them to two dimensionl arrays of given length [[1,2,3,4,5], [6,7,8,9,10]]
for (calls of test) {
Batchresults.push(await Promise.all(calls.map((call)=>{
fetch(`https://jsonplaceholder.typicode.com/posts/${call}`)
})
));
}
return Promise.all(Batchresults)
.then(function(results){
//call callback function here
promiseResolved();
}); //wait for all batch calls to finish
}
//assuming you have a callback function like this once all your batch calls finish
function promiseResolved(){
//set flagRunning to false
state.flagBatchRunning = false;
//if any pending requests are present, process them, else ignore
if(state.pendingRequestsArray.length > 0){
state.flagBatchRunning = true;
makeBatchCalls(pendingRequestsArray, pendingRequestsArray.length);
}
}
PS。这只是一个伪代码。不要在你的动作创建者中加入逻辑。应该通过reducer(更改状态)和saga / thunk来处理异步操作。
希望这有帮助。