In my react application, I have a Grid. User can select many grid rows at a time and click on a button to take bulk action on selected grid rows.
On a server side I
This is bound to happen because in your code, there is no check to see if already a batch request is running or not. You will have to make some changes in your code to accommodate batch calls correctly.
Step 1:
First of all, keep a flag in your state to see if already a batch request is running, say flagBatchRunning. Make it to true in your makeBatchCalls function before firing the requests.
Now once the Promise.all is resolved and all requests have completed, make it to false again.
In your action creator, check for this flag to be false.
function onGridRowsSelection(selectedRows) {
if(!state.flagBatchRunning){
makeBatchCalls(selectedRows,5)
}
}
Step 2:
Simply keeping a flag won't help you because it is quite possible that a user again clicks the bulk action button while your batch call is running and your onGridRowsSelection will ignore this update in this case. So, now you need to keep some kind of variable to store these pending batch requests.
To cater this, create an array say, pendingRequestsArray. Keep adding all your pending updates in this array and once previous batch is completed, pick all the requests from pending Array and make a batch call for them.
So your function now changes to this.
// Action creator, selectedRows is an array.
function onGridRowsSelection(selectedRows) {
if(!state.flagBatchRunning){
makeBatchCalls(selectedRows,5)
}else{
state.pendingRequestsArray.push(selectedRows); //push to pending array
}
}
async function makeBatchCalls(selectedRows, length) {
let test = arrayIds.reduce((rows, key, index) => (index % length == 0
? rows.push([key])
: rows[rows.length-1].push(key)) && rows, []);
let Batchresults = []; //convert them to two dimensionl arrays of given length [[1,2,3,4,5], [6,7,8,9,10]]
for (calls of test) {
Batchresults.push(await Promise.all(calls.map((call)=>{
fetch(`https://jsonplaceholder.typicode.com/posts/${call}`)
})
));
}
return Promise.all(Batchresults)
.then(function(results){
//call callback function here
promiseResolved();
}); //wait for all batch calls to finish
}
//assuming you have a callback function like this once all your batch calls finish
function promiseResolved(){
//set flagRunning to false
state.flagBatchRunning = false;
//if any pending requests are present, process them, else ignore
if(state.pendingRequestsArray.length > 0){
state.flagBatchRunning = true;
makeBatchCalls(pendingRequestsArray, pendingRequestsArray.length);
}
}
PS. This is just a pseudo code. Do not put logic in your action creator. It should be taken care of by reducer(to change state) and saga/thunk for async actions.
Hope this helps.
The async
module has a function for this: async.queue. First you define a task function. Then you give it a task - in your case, an array of rows and the action you want it to take. The task will be run, or added to the queue if there is already a task in progress. When a task is completed, the next one will be taken from the queue.
Better yet, you could define the task function for just one row and set the concurrency of the queue to 5. When the user clicks the button, you add lots of tasks to the queue, one for each row that was selected. 5 tasks will start running immediately, and the rest will be queued. This is probably better than what you're trying to do, because this way the user can start 2 tasks and then immediately start another 3, and they will all run in parallel.
Try the following code:
const async = require('async'); // or whatever mechanism you're using for module management.
const queue = async.queue((row, callback) => {
fetch(`https://jsonplaceholder.typicode.com/posts/${call}`)
.then(callback, callback);
}, 5);
function onGridRowsSelection(selectedRows) {
for (let call of selectedRows) {
queue.push(call);
}
}