Open kpruden opened 6 years ago
Wondering if there is any solution for this problem? I havn't thought out workaround in my scenario ...
Thank you for calling this out. This is due to using setTimeout
to queue pending callbacks on the event loop rather than maintaining an explicitly ordered queue. Several timeouts will expire as soon as enough tokens are available and there is a race to see which one claims the token(s) first, while the rest set new timeouts.
If someone wants to take a stab at refactoring this I would be happy to review / provide feedback / merge.
@kpruden @thinkhy @jhurliman
If you still looking for solution, rate-limiter-flexible package supports FIFO queue rate limiter. There is also migration guide, if you wish to migrate from limiter
.
I'm not in love with this solution, but I was able to order by calls by ignoring the execution order of limiter and manually increment an index. I thought I'd post it here in case somebody else finds this approach useful.
/**
* This performs concurrent operations using the provided limiter. This function ensures the array
* is processed in a FIFO order, which is not a feature of the limiter library.
*/
async function mapWithRateLimiter(array, limiter, callback) {
// We can't rely on the call order with limiter, so we have to manually increment the index.
let result = [];
let index = 0;
// Run the limiter the necessary number of times.
await Promise.all(_.times(array.length, async () => {
await limiter.removeTokens(1);
let currentIndex = index;
index++;
result[currentIndex] = await callback(array[currentIndex], currentIndex);
}));
return result;
}
It looks like maybe the callbacks provided to
removeTokens
can be called out of order. I'm not sure if ordering is an intended feature, but if not it'd be good call that out clearly in the docs.Running this code several times shows arbitrary ordering:
I can work around this limitation so this isn't an urgent issue for me. However if I'm misunderstanding something any pointers would be helpful. I have a slight suspicion that maybe
setTimeout
doesn't quite work the way I think it does :)