React - 控制对服务器的 AJAX 次调用

React - Controlling AJAX calls made to the server

在我的 React 应用程序中,我有一个参数数组(例如一些 ID),它们应该用作 ajax 调用队列的参数。问题是数组可能包含超过 1000 个项目,如果我只是使用 forEach 循环递归地进行 ajax 调用,浏览器页面最终会在每个请求得到解决之前停止响应。

有没有一个库可以发送ajax个请求,比如一次异步维护5个请求。

这是我现在使用的代码。

async function makeBatchCalls(arrayIds, length) 
{
     //convert arrayIds to two dimensional arrays of given length [[1,2,3,4,5], [6,7,8,9,10] ....]
     let test = arrayIds.reduce(
             (rows, key, index) => (index % length == 0 
                                    ? rows.push([key]) 
                                    : rows[rows.length-1].push(key)) && rows, []);

    let Batchresults = [];

    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
}

makeBatchCalls([1,2,3,4,5,6,7,8,9,10,12,12,13,14,15,16,17,18,19,20],5)

此代码的问题是它等待 5 个调用完成,然后发送另一批 5 个调用。这不是对网络的有效利用。我想要的是在任何时间点应该有 5 个请求。

是否可以调整上面的代码本身以满足要求?

这是一个需要解决的有趣问题。我能想到的一种方法是在第一批中的任何一个完成后立即进行 6th ajax 调用。这样,任何时候都会有 5 ajax 个请求在处理中。我试图实现类似的东西。虽然我的解决方案没有进行 ajax 调用,但我猜你可以更改 process 函数来进行 ajax 调用和 return 承诺。

JS Bin

/** 
  This function processes the jobs in batches, once a job is finished in batch it   then processes the next job. This can be used to make network calls.  
*/
function processBatch(queue, batchSize, processJob) {
  // remove a batch of items from the queue
  const items = queue.splice(0, batchSize);
  let count = items.length;

  // no more items?
  if (!count) {
    return Promise.resolve();
  }

  return new Promise((resolve, reject) => {
    return items.forEach((item, i) => {
      return processJob(item).then(result => {
        return processBatch(queue, 1, processJob)
          .then(_ => --count || resolve());
      });
    });
  })
}

// create queue
var queue = [];
for (var i = 1; i <= 20; i++) {
  queue.push(i);
}

// a per-item action
function process(item) {
  console.log('starting ' + item + '...');
  return new Promise((resolve, reject) => {
    // (simulating ajax)
    return setTimeout(function() {
      console.log('completed ' + item + '!');
      resolve();
    }, Math.random() * 1000);
  });
}

// start processing queue!
processBatch(queue, 5, process)
  .then(result => console.log("All jobs processed"));

我只是尝试使用 promises 实现一个通用函数。您可以尝试 运行 与 ajax 调用相同。我很想知道这个解决方案如何为您服务。

如您所见,我在成功执行每个作业后递归调用 processBatch 函数,并且连续的 batchSize 被硬编码为 1,但这可以更改并参数化。此外,此功能仅适用于快乐路径情况,因为它不考虑被拒绝的承诺。

有趣的问题,我将提出一个与你提出的不同的解决方案,这个解决方案将确保每次最多处理 5 个请求。

function makeBatchCalls(arrayIds, length) {
    // determines how many queries are being sent at any given time
    let queued = 0;
    // determines the index of the query to send at any given time
    let currentIndex = 0;
    // recursive function that launches queries and makes sure the queue is respected
    let launchNext = function() {
        if (queued === length) {
            return;
        }
        fetch(`https://jsonplaceholder.typicode.com/posts/${arrayIds[currentIndex]}`).then((results) => {
            queued--;
            launchNext();
            // do something with your results here...
        });
        queued++;
        currentIndex++;
    };
    // launch the first length queries as the queue is empty at first
    for (let i = 0; i < length; i++) {
        launchNext();
    }
}

希望对您有所帮助。

您可以将 Async 库用于您的用例。有一个队列函数可以做到这一点。它维护一个要执行的任务队列并执行它们,并始终保持所需的并发性。

以下是如何更改您的函数以使用异步队列。

async function makeBatchCalls(arrayIds, length) 
{
    // create a queue object with concurrency 5(equal to batch length in your case)
    var q = async.queue(function(task, callback) {
        //Call your api for this task here
        fetch(`https://jsonplaceholder.typicode.com/posts/${call}`)
        .then(function (response) {
            //Once your task executes successfully, call the Q callback so that next pending task can be picked up. 
            //This ensures all your tasks keep running one after the other with desired concurrency
            callback();
        })
        .catch(function (err) {
            //in case of failure, either you can return, or you can move to next task by calling callback
        });
    }, 5);

    // Is called when all the tasks have completed
    q.drain = function() {
        console.log('all items have been processed');
    };

    // push all items to queue
    for(var i=0; i < arrayIds.length; i++){
        q.push(arrayIds[i]);
    }
}

makeBatchCalls([1,2,3,4,5,6,7,8,9,10,12,12,13,14,15,16,17,18,19,20],5)