2017-09-26 54 views
3

在我的React应用程序中,我有一个参数数组(例如一些ID),它应该用作ajax调用队列的参数。问题是该数组可能包含超过1000个项目,如果我只是递归地使用forEach循环进行ajax调用,浏览器页面最终会在每个请求解析之前停止响应。React - 控制对服务器的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个请求。

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

+0

你可以重构你的'reduce'语句来使用多行,也许不是三元运算符吗?我无法理解你想要用它做什么。 –

+0

完成,请立即检查。 –

+0

我提供了一个答案,希望它有帮助。请找出时间查看答案并检查您认为最适合的答案。 –

回答

2

这是要解决的有趣问题。我能想到的一种方法是在第一批中的任何一个完成后立即致电6th ajax调用。这样就可以随时处理5个Ajax请求。我试图实现类似的东西。虽然我的解决方案不会打ajax电话,但我猜你可以更改process函数来进行ajax调用并返回答复。

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

我只是想实现使用承诺的通用函数。你可以试着用ajax调用运行它。我很想知道这个解决方案如何为你工作。

正如你所看到的,我递归调用每个作业的成功执行和连续batchSize是硬编码为1processBatch功能,但可以改变参数。而且,这个函数只适用于开心路径的情况,因为它没有考虑到被拒绝的承诺。

+0

这看起来很有前途(哈哈),实际上我正在为即将到来的项目变更做出承诺队列。肯定会给这个解决方案一个尝试 – clurect

+0

谢谢@ clurect。让我知道它是如何解决你的。 – shree33

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

希望这会有所帮助。

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)