4 ms·
I often map a series of X to Y with Promise.all. For example, mapping a series of image URIs to loaded Image elements. It is shorter to write than a for of loo
by mattdesl 3y ago
I often map a series of X to Y with Promise.all. For example, mapping a series of image URIs to loaded Image elements.
It is shorter to write than a for of loop, and importantly, all images will be loaded in parallel rather than sequentially, which can be significantly faster.
images = Promise.all(uris.map(loadImage))
- recursive 3y agoYou could also do it with a loop to start the promises and another one to await them. Is it just a shorter way of expressing that?
- jonathanlydall 3y agoAs per my other reply to you, there is a difference, a for loop will take the sum of each call to fetch the image while Promise.all will do the requests concurrently, meaning the total time will be that of the slowest request.
- recursive 3y agoAs per my other reply to your other reply, when I've written code that actually does `await` in a loop, it already does all the work concurrently. There's a code sample that illustrates what I'm familiar with. On the other hand, the language designers are not random framework authors. They know what they're doing. There must be some reason why `Promise.all` exists. I just don't know what it is. To re-iterate, I understand the difference between serial and parallel tasks. But I have also found that it's possible to do parallel tasks with `await` in a loop. So I'm still missing something.
- mattdesl 3y agoTo do parallel tasks in a loop for the situation I outlined (getting a final array of images from URIs), it would be really cumbersome, and your implementation would be pretty much equivalent to the inner workings of Promise.all. As others have mentioned; if you aren’t using Promise.all, you are likely missing a good deal of opportunities for easier and more performant async code.
- recursive 3y agoFor non-rejecting promises, `await Promise.all(promises)` is basically performance equivalent to `for (const p of promises) await p;`. In case you think the second one does serial work, here's a codepen for you. https://codepen.io/tomtheisen/pen/QWPOmjp https://codepen.io/tomtheisen/pen/QWPOmjp The "inner workings" could be a for loop, with the exception of rejected promises. The only opportunity for quicker resolution is when one of the promises reject, at which time Promise.all() immediately rejects. I think the allegations of "easier" are significantly overblown too.
- mattdesl 3y agoYou are right. I was mistakenly thinking that the alternative (naive approach) would be to write the promise-creating tasks within the for loop, but if they are not part of the loop, the functionality is basically equivalent to Promise.all. // A -- Async and parallel const results = await Promise.all(data.map((i) => delay(i))); // B -- Functionally equivalent to the above const promises = data.map((i) => delay(i)); const result = []; for (let a of promises) { const r = await a; result.push(r); } // C -- Problematic and naive approach: much slower const result = []; for (let i of data) { const r = await delay(i); result.push(r); }