7 ms·
Promises Training: Practical Exercises on Promises in JavaScript
- henriqueinonhe 3y agoRecently I've created a project to help people deepen their knowledge on Promises in Javascript beyond the basics by working through a series of practical exercises, where each is accompanied by a set of automated tests.
- moring 3y agoOne thing I have recently stumbled upon is that the way Promise.all() is commonly used is "wrong" in the sense that it is prone to uncatchable promise rejections (which cause both Node and Deno to exit the whole process by default). I would really like to see a tutorial on how to solve this the right way -- right now it seems that everybody does it the "wrong way" and the problem isn't even mentioned. Common pattern #1 (tuple-style): const result = await Promise.all([ someAsyncFunction1(computeArg1()), someAsyncFunction2(computeArg2()), ]); Common pattern #2 (array-map-style): const result = await Promise.all(elements.map(x => someAsyncFunction3(computeArg3(x)))); Both suffer from the same problem: If computeArg2 / computeArg3 potentially throw a (synchronous) error, then some promises have already been created, but Promise.all() never runs and so they don't have a catch-handler. If one of those rejects ("throws asynchronously") then that rejection is unhandled and crashes the process. The problem is also discussed here, but in the context of "should ECMAScript be changed": https://es.discourse.group/t/synchronous-exceptions-thrown-from-complex-expressions-create-abandoned-promises-solutions/663 https://es.discourse.group/t/synchronous-exceptions-thrown-f...
- EthicalSimilar 3y agoPromise.allSettled(…x) :)
- moring 3y agoThis doesn't solve the issue at all, does it? If Promise.all() is never reached, neither is Promise.allSettled().
- simonbarker87 3y agoallSettled() returns an array of objects for each promise result that tells you if it has been rejected or resolved, so as long as all the promises reject or resolve then it should solve the problem I think?
- Timon3 3y agomoring is describing an issue where the creation of the promise itself fails, which means Promise.all/Promise.allSettled is never called.
- evanreichard 3y agoUse the second pattern with `.allSettled` but map it with an async function. const result = await Promise.allSettled(elements.map(async x => someAsyncFunction3(computeArg3(x))));
- Timon3 3y agoYep, that works fine: https://www.typescriptlang.org/play?target=8#code/IYZwngdgxgBAZgV2gFwJYHsIxOgtgUwEFxoAxJKNTACmACcBzALhggVwCN86BKGAbwBQMGHXzIEdLMADuwVMhj0GggL6DBiFBixQ8ABwTIijWoxZtO3HhfZc6A4aPGTpjGAGoYARjUbQkLBalDrYeEQkUOTamADqCgAWAKJ0dOh0ZsysdtaOIsgJaTKs+MUpaRkARDgExIHRIZiVPH6aFFS6BkYmDPEF5emZtla8w-Z5MAVFJWWpg5V6uIbGhIzNrXoQIIr4ADb4BBDIIDAAvDAA2gAMADQ+dwBMALoaANy0kTDUfKcAfBObbbOEAIXbIbxnJRyBQwAAKaVwqBA+AAdMBdrsAMriZD7AAm1D2B3wRxAKNwwH0H0CMAAHmd-jUIvV2jpqItlj1qLSeLyWiJAeh9ijdugGNQxCCwd4Wk5AYpJaDkA9IbJ5Ip4Xgkaj0VicfjCftDsdyZTqdA6QywrVIg0On1knMMhzuqtxTy+XLMDhhaLxYqwQ9ZQLvQr8FLkABmVXQjUI7VojHY5C4-AEonGskUqkBC30v7W5lkVk0F0rRgOgYZD28r1bIWov0S8NKyMtVQ8b5AA https://www.typescriptlang.org/play?target=8#code/IYZwngdgxg...
- EthicalSimilar 3y ago
- deleted 3y ago[deleted]
- roblh 3y agoThat one is super interesting. For the first example, I'm not even really sure how I'd expect that to behave, it feels like no matter how it works it becomes slightly inconsistent with my expectations of the language. Like, if it were changed to somehow let a .catch grab the synchronous errors or have it somehow evaluate all of the compute functions synchronously first before making the promises, it still feels wrong. Maybe the only good solution is run the compute args separately beforehand? For the second example, it's kind of a weird one, but I'm actually a fan of using async .reduce to accomplish that. It took me a minute the first time I saw someone do it, but I think it more cleanly solves the problem and gives you more control over the result.
- polishdude20 3y agoHow would you use reduce for that?
- roblh 3y agoReduce can use an async function which wraps the collector in a promise and gives you a ton of control over how it executes because you can choose at what point you want the function to block waiting for previous results. In this particular case it's a lot more verbose, obviously, but if you're doing any kind of further processing of each element, you can move it into the reduce and be able to control exactly what you get out of it if something fails for any reason, while still running mostly concurrently like all or allSettled would. const result = await elements.reduce(async (collector, x) => { const arg3 = computeArg3(x) // explicitly handle whatever errors, try catch, whatever you need const asyncResult = await someAsyncFunction3(arg3) // this has to happen after calling someAsyncFunction because it will block until the previous iteration finishes collector = await collector; collector.push(result) return collector }, []);
- noctune 3y agoIn Scala there's Future.delegate for turning sync failures into failed futures. Seems like something equivalent should be possible in Javascript.
- seniorsassycat 3y agoThe answer is to never throw exceptions from promise returning functions. Mark all promise returning functions `async` - even when it doesn't `await` - because an async function always returns. https://typescript-eslint.io/rules/promise-function-async/ https://typescript-eslint.io/rules/promise-function-async/
- moring 3y agoThis only solves the second example (the "map" one), but not the first example because it doesn't contain any promise-returning function that throws.
- seniorsassycat 3y ago> doesn't contain any promise-returning function that throws Then what is the issue described in #1? Ahh, the compute arg calls
- Etheryte 3y agoWhat's up with all these weird disclaimers? > ATTENTION: DO NOT CLONE THIS REPO UNLESS YOU'RE CONTRIBUTING The license is Creative Commons so this doesn't really make any sense.
- AgentME 3y agoI think that's just meant to mean that cloning the repo isn't part of the tutorial. For the tutorial, you're meant to use the "npm create promises-training@latest" command to get the correct parts of the repo. The way it's formatted and lacking this context is definitely a bit too aggressive, as if you'll get in trouble with someone if you do want to clone the repo for other reasons.
- meindnoch 3y agoI'm gonna clone it.
- ranting-moth 3y agoI'm gonna clone it three times!
- deleted 3y ago[deleted]
- deleted 3y ago[deleted]
- henriqueinonhe 3y agoIt's as @AgentME said, it is just meant as a disclaimer that the project should be installed via the "installer", i.e. `npm create promises-training@latest`, otherwise the project won't work as expected. But I agree the phrasing is a little bit weird, will review it.
- austin-cheney 3y agoCurrently, promises are the de-facto way of handling asynchronous tasks in Javascript Not really. Most of the Node API is still reliant upon callbacks and the frontend absolutely doesn’t care. With one technical exception[1] the use of promises is purely stylistic. As someone old I still prefer callbacks because it’s just functions. One of the biggest frustrations with JavaScript is people attempting to impose their opinions on you. If you prefer promises then just use promises. The language provides 3 options for asynchronous handling and doesn’t care what path you take. [1] A promise chain cannot be cancelled or exited early without a throw.
- pavlov 3y agoPromises are now enshrined in the language syntax through the async / await keywords, so I would say they're increasingly unavoidable.
- beeboobaa 3y ago> The language provides 3 options for asynchronous handling and doesn’t care what path you take. The language actually just provides one: async/await.
- austin-cheney 3y agocallbacks, promises, async/await. That is 3.
- ayewo 3y agoAre there similar resources for learning promises that use a different style of instruction out there? The writing feels a bit off to me.
- ranting-moth 3y agoSomeone has gone through a massive effort of creating this. I'm guessing that he's all in for constructive criticism. Asking for something else and offering only "The writing feels a bit off to me" isn't very helpful or polite IMHO.
- ayewo 3y agoI apologize to the author if my comment comes across as impolite. I really wanted to bookmark it for further reading since its a topic I’ve been meaning to dive deeper into. I was happy that the target audience wasn’t beginners since it assumes familiarity with promises and async/await. But I kept parsing the numerous “warnings” and the writing style in a way that made me feel that perhaps I’m not the target audience. I’ve come to realize that I am much more likely to make the most out of content when the writing style gels with how I think.
- henriqueinonhe 3y agoI'd like to know why the writing "feels off" for you, I'm definitely willing to improve it. If it makes you feel more comfortable, you may DM me here or any other social network (e.g. Github).
- simonbarker87 3y agoNot the GP but it could be the length of your sentences. I personally think it reads fine however, since I also write with pretty long, heavily punctuated, sentences I’m comfortable reading it and it feels natural to me. The biggest complaint people have about my writing is the length of the sentences and feeling like I’m expressing too many ideas at a time.
- dagurp 3y ago
- Yoric 3y agoI remember me running a tutorial on Promise for my team back in 2011. Those were the days :)
- anamexis 3y agoBluebird.js wasn't even released until 2013, what implementation were you using?
- Yoric 3y agoThree Firefox developers from three different teams (Paolo, Irakli and I) each came up with Promise (with slightly different name and semantics - I think that mine was called Future, for instance). We were using them behind the scenes to refactor Firefox and make its UX async. Of course, when we discovered that there were three implementations, we merged them. Later, Irakli led conversations with (if my memory serves) two other teams who had also come up with Promise on the web or on Node, and we eventually merged with these proposals. Similarly, my team was using async/await in 2014 or 2015 (it was called `Task` at the time), a few years before it was standardized. I believe that Paolo was the first person in the world to ship production code with async/await and me the second.
- anamexis 3y agoVery cool!
- henriqueinonhe 3y agoDo you still have it?
- Yoric 3y agoThat was on a whiteboard. I could try and find the list of people who were in the room, if necessary :)
- test_account01 3y ago[flagged]