2

I have a set of n users where I need to hit an API endpoint for each user to get more information, which is quite easy with Promise.all(), but now my n has gotten to be much bigger and I now need to throttle my calls to about 50 per 5 seconds. Is there a way using .reduce() combined with setTimeout(() => {}, 5000) to batch 50 calls every 5 seconds, but still capture the results of every call in Promise.all().then()?

// Retrieve data from an API endpoint for n users [works]
const users = [...array of 50 ids];

Promise.all(users.map(user => axios.get(`api/users/${user.id})))
       .then(results => resolve())
       .catch(err => reject(err));
hotshotiguana
  • 1,520
  • 2
  • 26
  • 40
  • Just for curiosity. Why do you make request for each user? You can request all users by one request and then distribute by id. What benefit has making so many requests? Btw I already answered to [similar question](https://stackoverflow.com/questions/48432121/burst-control-over-multiple-ajax-requests/48433058#48433058) so maybe you find there inspiration.. – bigless Feb 09 '18 at 20:57
  • Unsure on your comment. `user.map(...)` is creating an array of requests which is then parallelized at the same time by `Promise.all()`. I'm hitting the Slack API, which only takes one `user_id` at a time. Oh it looks like I made a mistake in copying the code over to SO... – hotshotiguana Feb 11 '18 at 21:50
  • This looks a lot like the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi Feb 11 '18 at 21:58
  • @Bergi thanks for pointing out the anti-pattern...helped me better understand promises and tidied up my code quite a bit! – hotshotiguana Feb 13 '18 at 02:44

2 Answers2

1

you could do something like this, this is slightly pseudo, but i am sure you get the gist of it :):

const batchSize = 50;
const promises = [];
const users = [...lots of users];
const userCount =  users.length;
let userIdx=0;

function getUsers() { 
   for(let i=0;i<batchSize;i++) {
      if(userCount < ++userIdx) {
        Promise.all(promises).then(doYourStuff);
        return;
     }
     promises.push(axios.get('api/users/' + users[userIdx]));
   }       
   setTimeout(_ => getUsers(), 5000);
}
Martin Jespersen
  • 25,743
  • 8
  • 56
  • 68
1

Thanks! I tested it and did the splice and map to clean it up a bit, but it does appear to work as intended.

const batchSize = 2;
const promises = [];
const users = ['a', 'b', 'c', 'd', 'e', 'f'];

function asyncTest(item) {
    return new Promise((resolve, reject) => {
        process.nextTick(() => {
            console.log(item);
            resolve(item);
        })
    })
}

function getUsers(xs) {
    if (!xs.length) {
        console.log("running 'em all");
        Promise.all(promises);
        return;
    }
    promises.push(xs.splice(0, batchSize).map(asyncTest));
    setTimeout(_ => getUsers(xs), 500);
}

getUsers(users);
hotshotiguana
  • 1,520
  • 2
  • 26
  • 40