How to execute promises sequentially, passing the parameters from an array?
You can make the repeated application of .then
into a fold pretty neatly if you’re okay with creating as many promises as array elements as is the case in the question:
myArray.reduce(
(p, x) =>
p.then(() => myPromise(x)),
Promise.resolve()
)
but given support, an async function is a better choice. It’s nicely readable and has O(1) instead of O(n) memory overhead.
const forEachSeries = async (iterable, action) => {
for (const x of iterable) {
await action(x)
}
}
forEachSeries(myArray, myPromise)
If you want to collect the return values as an array, that’s:
const mapSeries = async (iterable, fn) => {
const results = []
for (const x of iterable) {
results.push(await fn(x))
}
return results
}
or, without async function support,
const mapSeries = (iterable, fn) => {
const iterator = iterable[Symbol.iterator]()
const results = []
const go = () => {
const {value, done} = iterator.next()
if (done) {
return results
}
return fn(value).then(mapped => {
results.push(mapped)
return go()
})
}
return Promise.resolve().then(go)
}
Runnable snippet:
const myArray = [1, 2, 3, 4, 5, 6]
const sleep = ms =>
new Promise(res => {
setTimeout(res, ms)
})
const myPromise = num =>
sleep(500).then(() => {
console.log('done: ' + num)
})
const forEachSeries = async (iterable, action) => {
for (const x of iterable) {
await action(x)
}
}
forEachSeries(myArray, myPromise)
.then(() => {
console.log('all done!')
})
Don't create an array of promises. Create an array of functions returning a promise.
const f = x => new Promise(resolve => setTimeout(() => resolve(console.log(x)), 2000))
(async () => {
for (let job of [1, 2, 3, 4, 5, 6].map(x => () => f(x)))
await job()
})()
Promises start running immediately after creation. Therefore, sequential execution is ensured by constructing the next promise only after finishing the current one.