Are you falling into this? Promise.all
Pitfall? you know it. Get a list from somewhere and run a parallel function on all of them.
const list = await getMeSomeList()
const results = await Promise.all(list.map(someAsyncFunction))
It works fine when the list contains a few things, but let’s say it suddenly returns 10,000 records. This can get very messy.
You’re trying to spin too many plates and you’re running out of memory or resources…
solution
Well, you just need to install the async package, which has a lot of useful features, such as: mapLimit
This reduces the burden and only does some parallel execution.
If that is excessive, you can use a simple rate limiter to achieve a similar result.
class Semaphore
constructor(maxConcurrency)
this.maxConcurrency = maxConcurrency
this.currentConcurrency = 0
this.queue = []
async acquire()
return new Promise((resolve) =>
if (this.currentConcurrency < this.maxConcurrency)
this.currentConcurrency++
resolve()
else
this.queue.push(resolve)
)
release()
if (this.queue.length > 0)
const resolve = this.queue.shift()
resolve()
else
this.currentConcurrency--
export function rateLimit(asyncFunction, rate)
const semaphore = new Semaphore(rate)
return async function process(...args)
await semaphore.acquire()
try
return await asyncFunction(...args)
finally
semaphore.release()
With this in place, the code changes to:
const list = await getMeSomeList()
const results = await Promise.all(list.map(rateLimit(someAsyncFunction, 20))
This means it will continue to run 20 at a time until the listway is finished.Each time, one of the someAsyncFunction
Returning s will start another one until the list runs out. It’s easy 🙂