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

时间:2017-09-26 17:30:09

标签: javascript ajax reactjs promise async.js

在我的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个请求。

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

3 个答案:

答案 0 :(得分:2)

这是一个有趣的问题需要解决。我能想到的一种方法是在第一批中的任何一个完成后立即进行6th 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"));
&#13;
&#13;
&#13;

我只是尝试使用promises实现泛型函数。您可以尝试使用ajax调用运行相同的操作。我很想知道这个解决方案对你有用。

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

答案 1 :(得分: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();
    }
}

希望这有帮助。

答案 2 :(得分:1)

您可以将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)