React - 控制多个Ajax调用

时间:2017-09-16 08:31:39

标签: javascript ajax reactjs promise axios

在我的反应应用程序中,我有一个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
}

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

  1. 从网格中选择5行以上,然后点击批量操作按钮
  2. 再次选择超过5行,然后点击批量操作按钮,
  3. 现在我看到一次有10个请求处于活动状态。
  4. 我该如何限制?

    React - controlling async calls smartly without any side effect in complex applications

    中询问了此处提到的问题的后续问题

    这个问题是JavaScript, React - sending multiple simultaneous ajax calls

    的后续问题

2 个答案:

答案 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来处理异步操作。

希望这有帮助。