React - 控制多个 Ajax 调用

React - Controlling multiple Ajax Calls

在我的 React 应用程序中,我有一个网格。用户可以一次 select 多个网格行,然后单击按钮对 select 编辑的网格行执行批量操作。

在服务器端,我有一个脚本,我想为每个 selected 行执行(为了简化问题,我在下面的示例中为每个 selected 行)点击批量操作按钮。单击批量操作按钮时,我在操作创建器中得到 selectedRows,我在其中迭代 selectedRows 并对每个 selected 行进行 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
}

上面的解决方案工作正常,但有一个问题,

  1. Select 网格中超过 5 行并单击批量操作按钮,
  2. 再次 select 超过 5 行并单击批量操作按钮,
  3. 现在我看到一次有 10 个请求处于活动状态。

我该如何限制它?

中询问此处提到的问题的跟进问题

此问题是

的跟进问题

这肯定会发生,因为在您的代码中,没有检查批处理请求是否已经是 运行。您必须对代码进行一些更改才能正确适应批处理调用。

第 1 步:

首先,在你的状态中保留一个标志,看看是否已经有批处理请求 运行,比如 flagBatchRunning。在触发请求之前,在 makeBatchCalls 函数中将其设置为 true。

现在一旦 Promise.all 得到解决并且所有请求都已完成,再次将其设置为 false。

在您的动作创建器中,检查此标志是否为假。

function onGridRowsSelection(selectedRows) {
  if(!state.flagBatchRunning){
    makeBatchCalls(selectedRows,5)
  }
}

第 2 步:

简单地保留标志对您没有帮助,因为当您的批处理调用 运行 和您的 onGridRowsSelection[=38= 时,用户很可能再次单击批量操作按钮] 在这种情况下将忽略此更新。因此,现在您需要保留某种变量来存储这些待处理的批处理请求。

为了解决这个问题,创建一个数组,比如 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 处理异步操作。

希望对您有所帮助。

async 模块为此提供了一个函数:async.queue。首先定义一个任务函数。然后你给它一个任务 - 在你的例子中,一组行和你想要它采取的行动。该任务将 运行,如果已有任务在进行中,则将其添加到队列中。当一个任务完成后,会从队列中取出下一个任务。

更好的是,您可以只为一行定义任务函数,并将队列的并发设置为 5。当用户单击该按钮时,您向队列中添加了很多任务,每行一个任务选择。 5个任务会立即运行ning开始,剩下的会排队。这可能比您尝试做的更好,因为这样用户可以开始 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);
    }
}