2017-09-26 53 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)