The following Typescript performs each call to doSomething(action) one at a time. (Meaning the second item in the list does not get a call made until the first
Jeff Bowman has vastly improved his answer to resolve meaningful values. Feel free to view the history of this answer to understand why the resolved values are so important/useful.
This solution closely mimics the native Promise.all
How it's the same …
How it's different …
// throttlep :: Number -> [(* -> Promise)]
const throttlep = n=> Ps=>
new Promise ((pass, fail)=> {
// r is the number of promises, xs is final resolved value
let r = Ps.length, xs = []
// decrement r, save the resolved value in position i, run the next promise
let next = i=> x=> (r--, xs[i] = x, run(Ps[n], n++))
// if r is 0, we can resolve the final value xs, otherwise chain next
let run = (P,i)=> r === 0 ? pass(xs) : P().then(next(i), fail)
// initialize by running the first n promises
Ps.slice(0,n).forEach(run)
})
// -----------------------------------------------------
// make sure it works
// delay :: (String, Number) -> (* -> Promise)
const delay = (id, ms)=>
new Promise (pass=> {
console.log (`running: ${id}`)
setTimeout(pass, ms, id)
})
// ps :: [(* -> Promise)]
let ps = new Array(10)
for (let i = 0; i < 10; i++) {
ps[i] = () => delay(i, Math.random() * 3000)
}
// run a limit of 3 promises in parallel
// the first error will reject the entire pool
throttlep (3) (ps) .then (
xs => console.log ('result:', xs),
err=> console.log ('error:', err.message)
)
Inputs are run in order; Resolved results are in the same order as the inputs
running: 0
running: 1
running: 2
=> Promise {}
running: 3
running: 4
running: 5
running: 6
running: 7
running: 8
running: 9
result: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Let's look at a more practical code example. This code is tasked with fetching a set of images from a server. This is how we might use throttlep to throttle the amount of simultaneous requests to 3 at a time
// getImage :: String -> Promise
let getImage = url=> makeRequest(url).then(data => data.base64, reqErrorHandler)
// actions :: [(* -> Promise)]
let actions = [
()=> getImage('one.jpg'),
()=> getImage('two.jpg'),
()=> getImage('three.jpg'),
()=> getImage('four.jpg'),
()=> getImage('five.jpg')
]
// throttle the actions then do something...
throttlep (3) (actions) .then(results => {
// results are guaranteed to be ordered the same as the input array
console.log(results)
// [, , , , ]
})