6 ms·
I assumed the Promises was added primarily so that async/await could be added, which is where the really substantial quality of life improvement is. In practice
by jonathanlydall 3y ago
I assumed the Promises was added primarily so that async/await could be added, which is where the really substantial quality of life improvement is. In practice you rarely need to explicitly go “new Promise” yourself.
While .then in initial promises was a great improvement over nested delegates and is fine for simple chained promises, once you start conditionally chaining different promises or need different error handling for particular chains in the promise, or wanting to do an early return, the code can become much harder to read and work with.
With async/await though you just write the call essentially as if it’s not a promise and can easily put try/catch around particular promise calls, easily have early returns, etc.
- esperent 3y ago> In practice you rarely need to explicitly go “new Promise” yourself However you do quite often need to use Promise.all, even when using async/await.
- recursive 3y agoI've literally never needed to do that. What's a real world use case?
- 3m 3y agoDoing multiple async tasks concurrently as opposed to in sequence. If you have never used this you have either worked on extremely simple systems or have been leaving a ton of perf gains on the table.
- Capricorn2481 3y agoOr, like most people, they don't work with JS outside of the frontend where fetching multiple things in parallel is rarely needed.
- recursive 3y agoHow is this different from await a, await b, await c?
- jonathanlydall 3y agoIf each await was to a setTimeout call waiting 1000ms, awaiting all 3 would take approximately 3000ms. If you await a Promise.all with an array of the promises, it will take approximately 1000ms. In summary, using individual awaits runs them serially, while Promise.all runs them concurrently. If you’re doing CPU bound work without workers, it doesn’t make much of a difference, but if you’re doing I/O bound tasks, like HTTP requests, then doing it in parallel will likely make a significant difference.
- recursive 3y agoI get what you're saying, but that's just not how it works in my experience. I've got a repro in codepen. What am I missing? https://codepen.io/tomtheisen/pen/QWPOmjp https://codepen.io/tomtheisen/pen/QWPOmjp function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function test() { const start = new Date; const promises = Array(3).fill(null).map(() => delay(1000)); for (const p of promises) await p; const end = new Date; console.log("elapsed", end - start); // shows about 1010 } test();
- jonathanlydall 3y agoWhat I imagined from your initial description is that you were doing the following: for (var i = 0; i < 3; i++) { await delay(1000); } However (as you are possibly well aware), this line in your example is starting all the work immediately and essentially in parallel: const promises = Array(3).fill(null).map(() => delay(1000)); So the timing of your for...of loop is that the first element probably takes about 1000ms to complete, and then the other two seem to happen instantly. Promise.all is just an alternative to writing the for...of await loop: await Promise.all(promises); I guess it relies on you already being familiar with the Promise API, but I feel that Promise.all() has slightly less cognitive load to read and its intent is more immediately clear. A strong case for preferring the Promise.all() is that Promise.allSettled(), Promise.any() and Promise.race() also exist for working with collections of promises, and unlike Promise.all(), they would not be so easily reproduced with a one liner for...of loop, so its not unreasonable to expect that JS developers should be aware of Promise.all(), meaning there is no reason for it not to be the preferred syntax for the reasons I stated above.
- mattdesl 3y agoI 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.
- MaxBarraclough 3y agoI don't think that's an issue with JavaScript though. It's inherent complexity in your code's handling of concurrent operations, not incidental complexity arising from the language.
- 9dev 3y agoAlso, wrapping callback APIs or containing side effects: function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } await sleep(100); There are some use cases for the Promise class still, and it’s great to have that kind of control facility at hand when you need it.
- M2Ys4U 3y agoPromise.withResolvers can fill this gap now (though it's not natively available in Node, yet): function sleep(ms) { const { promise, resolve } = Promise.withResolvers(); return setTimeout(resolve, ms), promise; }