【问题标题】:React - Controlling multiple Ajax CallsReact - 控制多个 Ajax 调用
【发布时间】:2018-02-25 08:55:04
【问题描述】:

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

在服务器端,我有一个脚本,我想为每个选定的行执行(为了简单起见,我在下面的示例中为每个选定的行调用“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 个请求处于活动状态。

我该如何限制这个?

React - controlling async calls smartly without any side effect in complex applications中询问此处提到的问题的后续问题

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

【问题讨论】:

    标签: javascript ajax reactjs promise axios


    【解决方案1】:

    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);
        }
    }
    

    【讨论】:

    • 谢谢你的回答,让我试试这个。
    【解决方案2】:

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

    第 1 步:

    首先,在您的状态中保留一个标志以查看是否已经有批处理请求正在运行,例如 flagBatchRunning。在触发请求之前,在您的 ma​​keBatchCalls 函数中将其设为 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 处理异步操作。

    希望这会有所帮助。

    【讨论】:

    • 1.谁会调用这个回调?
    • 我无法区分每次按钮点击的响应。
    • 1.一旦你的所有承诺都得到解决,就应该调用这个回调。检查下一组更新。更新了我的答案以显示如何调用回调。
    猜你喜欢
    • 1970-01-01
    • 2020-05-27
    • 1970-01-01
    • 2020-08-22
    • 2018-06-30
    • 2015-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多