Reputation: 3161
Whenever you see Promise.all() being used, it is usually used with two loops, how can I make it into only one without using async and mantaining execution order?
The question is not about the order, I know promise.all preserves order, the question is about how to avoid two loops when you just need the returned value
function timeout(x){
return new Promise( resolve => {
setTimeout( () => {
return resolve(x);
},x)
})
}
const promises = [];
const results = [];
//First loop, array creation
for (i = 0; i < 20; i++) {
const promise = timeout(i*100)
promises.push(promise);
}
Promise.all(promises).then( resolvedP => {
//Second loop, async result handling
resolvedP.forEach( (timeout,i) => {
results.push({
index : i,
timeout : timeout
})
})
console.log(results);
})
//End of code
Now, this can be solved with async
but in my context I can't use it, for example :
//Only one loop
for (i = 0; i < 20; i++) {
const timeoutAwait = await timeout(i*100);
results.push({
index : i,
timeout : timeoutAwait
})
}
console.log(results)
//End of code
What I have tried is the following, but the promise doesn't return the resolve
value without using .then()
:
for (i = 0; i < 20; i++) {
const promise = timeout(i*100)
promises.push(promise);
results.push({index : i, timeout : promise});
}
Promise.all(promises).then( resolvedP => {
resolvedP.forEach( (timeout,i) => {
results.push({
index : i,
timeout : timeout
})
})
console.log(results);
//results[0].timeout is a Promise object instead of 0
})
//End of code
So, is there any way I can make my first code sample in only one loop? Please ignore the context, is only an example.
Upvotes: 1
Views: 362
Reputation: 66500
As by the Promise.all
documentation, the order will be preserved. It says about the return value:
A pending Promise in all other cases. This returned promise is then resolved/rejected asynchronously (as soon as the stack is empty) when all the promises in the given iterable have resolved, or if any of the promises reject. See the example about "Asynchronicity or synchronicity of Promise.all" below. Returned values will be in order of the Promises passed, regardless of completion order.
(Highlighted by me.)
Upvotes: 1
Reputation: 39270
I find it more easy to understand if you map over an array of values instead of looping:
const timeout = x=>Promise.resolve(x);
Promise.all(
[...new Array(3).keys()]//array [0,1,2]
.map(x=>timeout(x*100))//[timeout(0*100),timeout(1*100),...
).then(
result=>result.map(//results of the timeout, map to object
(timeout,index)=>({
index,
timeout
})
)
).then(
result=>console.log('result:',result)
)
It's usually not a good idea to asynchronously mutate a shared variable (the results array). Just have the promise resolve and create the result you need in the last .then
, now your promise resolves to the value you want.
Upvotes: 0
Reputation: 8060
function timeout(x) {
return new Promise(resolve => {
setTimeout(() => {
return resolve(x);
}, x);
});
}
const promises = [];
const results = [];
//First loop, array creation
for (let i = 0; i < 20; i++) {
const promise = timeout(i * 100).then(x => results.push({
index: i,
timeout: x
}));
promises.push(promise);
}
Promise.all(promises).then(() => {
console.log(results);
});
If you want to preserve execution/results order assign results using i
index instead of .push
const promise = timeout(i * 100).then(x => results[i] = {
index: i,
timeout: x
});
Upvotes: 1