React - 控制向服务器发起的AJAX请求

3
在我的React应用程序中,我有一个参数数组(例如一些ID),这些参数应该作为ajax调用队列的参数使用。问题在于,数组可能包含1000多个项目,如果我仅仅使用forEach循环递归地进行Ajax调用,那么在每个请求都得到解决之前,浏览器页面最终会停止响应。
是否有一个库可以允许异步地维护5个请求,例如发送Ajax请求?
以下是我现在正在使用的代码。
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个请求。

是否可能调整上面的代码以满足需求?


你能否重构一下你的reduce语句,使用多于一行的代码,并且不使用三元运算符吗?我有些难以理解你的意图。 - Ghassen Louhaichi
完成,请现在检查。 - ThinkGeek
我提供了一个答案,希望能有所帮助。请抽出时间查看答案并选择您认为最合适的答案。 - Ghassen Louhaichi
3个回答

2
有趣的问题,我将提出与您提出的不同的解决方案,这个解决方案将确保每次最多处理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();
    }
}

希望这能有所帮助。

2
这是一个有趣的问题需要解决。我可以想到一种方法,即在第一批中的任何一个请求完成时立即进行第六个 ajax 请求。这样每次都会有 5 个 ajax 请求在进行中。我尝试实现了类似的东西。尽管我的解决方案不会发起 ajax 请求,但我猜想您可以更改 process 函数以发起 ajax 请求并返回 promise。

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,但是它可以被改变和参数化。此外,该函数只适用于成功路径情况,因为它不考虑拒绝的 promises。

这看起来很有前途(哈哈),我实际上正在寻找为我们项目即将到来的更改创建一个 Promise 队列。一定会尝试这个解决方案。 - clurect
谢谢@clurect。让我知道它对你有什么作用。 - shriidhar

1
您可以使用Async库来解决您的问题。该库提供了一个队列函数,它可以维护要执行的任务队列,并在任何时候保持所需的并发性来执行它们。
以下是如何修改您的函数以使用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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接