0

I have this function. simIds array length is 1000(unknown). So I need to make it like that it can promisify 50 at a time and then go for the next 950 and so on. So how can I make it like that. Thanks!!!

if(simIds.length < 0) {
  simIds.forEach((simid) => {
    promises.push(
      new Promise(function (resolve) {
      simService.getSimcardBasedonAction(userid, [simid], action)
      .then((result) => {
          if (result.success) {
          const simDetail = result.simcards[0];
          resolve(result)
        }
      })
    }));
  });
  Promise.all(promises)
  .then((result) => {
    console.log('result', result)
  })
}
Profer
  • 553
  • 8
  • 40
  • 81
  • BTW, there is no need to wrap your call `getSimcardBasedonAction` in another Promise, it is already a promise. – Jamiec Aug 18 '21 at 14:56

1 Answers1

2

Given a method which takes an array of items, a batchSize and a method which turns 1 item into a promise, you can simply batch them up and await the result

const batchPromisify = async (arr, batchSize, makePromise) => {
    while(arr.length) {
        const batch = arr.splice(0,batchSize);        
        const promises = batch.map(makePromise);            
        await Promise.all(promises);
    }
}

Here is a live example with an array of integers, and an async method which just simulates a short delay. You can see that the makePromise callback passed in just wraps the integer from the array in a delay followed by a console log saying that item is complete. I have also wrapped the call to the batch with console logs so you can see that it is running 10 at a time in this example:

const delay = (ms) => new Promise(resolve => setTimeout(resolve,ms));

const items = new Array(100).fill(0).map ( (_,i) => i);


const batchPromisify = async (arr, batchSize, makePromise) => {
    while(arr.length) {
        const batch = arr.splice(0,batchSize);        
        const promises = batch.map(makePromise);
        
        console.log("starting batch")
        await Promise.all(promises);
        console.log("finished batch")
    }
}

batchPromisify(items, 10, i => delay(100).then(_ => console.log("finished", i)));

Using this with your code would look something like

batchPromisify(simIds, 50, 
    simId => simService.getSimcardBasedonAction(userid, [simid], action))

There is no need to wrap your call in another Promise as getSimcardBasedonAction already returns one!


Now say you wwanted to get the results out afterwards, there is a small change that can be made to batchPromisify:

const delay = (ms) => new Promise(resolve => setTimeout(resolve,ms));

const items = new Array(100).fill(0).map ( (_,i) => i);


const batchPromisify = async (arr, batchSize, makePromise) => {
    const results = [];
    while(arr.length) {
        const batch = arr.splice(0,batchSize);        
        const promises = batch.map(makePromise);
        
        results.push(await Promise.all(promises));
    }
    return results;
}

(async function(){
  const result = await batchPromisify(items, 10, i => delay(100).then(_ => i.toString()));
  console.log(result.flat());
})();

Note in this case I have had to await the call to batchPromisify, and I changed the makePromise return the string equivalent of the integer for demonstration purposes. The result of the call is an array of arrays, representing each batch and the results therein. You can always use .flat() to flatten this array if you want all the results in 1 array.

Now your code would be

    const results = await batchPromisify(simIds, 50, 
             simId => simService.getSimcardBasedonAction(userid, [simid], action));
    const flatResults = results.flat();
Jamiec
  • 133,658
  • 13
  • 134
  • 193