6 ms·
A brief look at async-await
- cyrusmg 7y agoThere should be some review process for these .christmas articles. I get it - creating tons of articles quickly is hard, but the core message should not be incorrect. I tried to look up a github reference on the page, so I could send a PR to fix the linked article, but it is not available.
- sveingjoby 7y agoWe decided to keep the content in a private repo until every article is posted. If you could share what you found incorrect, we are happy to update the article accordingly
- cyrusmg 7y ago> "thus forcing the code to be synchronous" This (incorrect) message is then repeated again ("and act synchronously"). There are some other - minor - issues with rest of the article. I.e. asyncFunction and someFunction are not equivalent and as such can cause confusion (the same issue is in waitAndCheck vs chain.
- sveingjoby 7y agoUpdate the article based on your feedback. Additionally, we included a link to the "Top level await" proposal. Currently stage 3.
- gbuk2013 7y agoI appreciate that the author is trying to illustrate something with an intentionally contrived example, but there is really no need for "await" in his "chain()" function: function chain () { (function loop (i) { if (i > 5) { // set state to failed return; } check().then((result) => { if (!result) { return loop(i + 1); } // set state to finished }).catch((error) => { // set state to failed }); }(0)); } This is one of my pet peeve with proponents of promises actually. They come up with convoluted examples to argue against callbacks but it's not the callbacks that are a problem, but the person writing the code. Actually, I would argue that promises really made no sense until "await" became available because they introduce extra complexity and (small) performance penalty for no added benefit. Worse, it encourages less experienced developers to write code in a serial manner in situations where this is not necessary (a much bigger performance problem). Even with "await", the only time when it is really useful is when there have to be a several asynchronous operations that must happen serially because they use the result of the previous call. It depends on your field of work, of course, but in my experience these situations are really not that common. Some time ago I wrote an article comparing performance of callbacks vs async/await but it is also an example to show that callback code does not have to be more tedious to write: https://gir.me.uk/posts/node-8-async-await-performance-test.html https://gir.me.uk/posts/node-8-async-await-performance-test....
- koolba 7y ago> This is one of my pet peeve with proponents of promises actually. They come up with convoluted examples to argue against callbacks but it's not the callbacks that are a problem, but the person writing the code. While I agree the author’s examples are convoluted, the async-await code is much easier to read, write, and validate. Why bother tracking state and having to keep tracking of JS lexical scoping when you can write "boring" code that will be just as fast for the tiny iteration counts you'll be dealing with? > Even with "await", the only time when it is really useful is when there have to be a several asynchronous operations that must happen serially because they use the result of the previous call. It depends on your field of work, but in my experience these situations are really no that common. Concurrent tasks are more pleasant with await as you can combine it with Promise.all(...) to get efficiency and legibility: async function doStuff() { // Fetch things concurrently const [ foos, bars, bazs, ] = await Promise.all([ getFoos(), getBars(), getBazs(), ]); // Do stuff with them... }
- gbuk2013 7y ago> the async-await code is much easier to read, write, and validate. With the exception of necessarily serial code that I referred to, this statement is not true in my experience. Your "legible" example is only legible because you are not doing anything actually important like error handling and logging. In the real world each call may need different logging or even error handling logic and that is when promises really turn into a rats nest. Validation, i.e. tests, are really the same regardless which approach you are using. I do agree that async/await made promises actually usable, as I said above. For running a bunch of asynchronous functions that do the same thing the semaphore + collector pattern works just fine and is not much more verbose.
- Klathmon 7y ago>Your "legible" example is only legible because you are not doing anything actually important like error handling and logging. async function doStuff() { try { // Fetch things concurrently const [ foos, bars, bazs, ] = await Promise.all([ getFoos().then((res) => { console.log(`getFoos done with ${res}` return res })), getBars(), getBazs() .catch((err) => { // handle only the error in getBazs }), ]); // Do stuff with them... } catch (err) { // handle any errors } } it still isn't what i'd call "pretty" code, but it's simpler for me to read and comprehend than doing most of those things without async/await or Promise.all or other helpers like that.
- quietbritishjim 7y agoPresumably the "// set state to finished" involves a return statement, otherwise "check()" will continue to be called and ultimately the state will be set back to not done. This applies to both the async version and the chain() version. Personally I find it confusing that the comment hides a control flow statement when the text of the comment suggests that it's just setting a state variable. Surely a better comparison for the async function would be a chain() that manually recreates the loop using a parameter or local captured variable, like shown below. They're still complex but if anything show the complexity more clearly, especially if 6 is not hardcoded but could be passed as a parameter. I suppose having the unrolled version from the article plus one of these would be best of all, to show the problem from all angles. function chainWithParam() { var checkAndIterate; checkAndIterate = (result, i) => { if (result) { // set state to finished return; } if (i == 6) { // set state to not done return; } check() .then(result => checkAndIterate(result, i + 1)) .catch(error => /* set state to failed */); } checkAndIterate(false, 0); } function chainWithCapturedLocal() { i = 0; var checkAndIterate; checkAndIterate = result => { if (result) { // set state to finished return; } if (i == 6) { // set state to not done return; } ++i; check() .then(checkAndIterate) .catch(error => /* set state to failed */); } checkAndIterate(false); } I'm not a JavaScript programmer so there could be mistakes in the above code. I'd be interested to know if there is, especially whether it was really necessary to declare the checkAndIterate variable on a separate line to its assignment. Surely the "manual" function could be implemented with some sort of loop-like
- mywittyname 7y agoI think most JS programmers do something along the lines of... function check() { return new Promise(async (resolve, reject) => { for(let i = 0; i < 6; ++i) { if(await doCheck()) { resolve(true); } } reject(); }); } or... async function check() { for(let i = 0; i < 6; ++i) { const status = await doCheck(); if(status) { return status; } } throw new Exception('failed the 6 checks'); } Though, the same thing could certainly be accomplished with pure promise chains. One could also use the setTimeout method in situations where elapsed time is more important than a fixed number of calls.
- laurent123456 7y agoThe article would have been better without the last example. It doesn't clarify much and it's not something that anyone should ever write.
- mywittyname 7y agoIt serves as a nice reminder that having a blog does not make a person an authority.
- TheSoftwareGuy 7y agoI know its kind of meta, but what's with all the blog posts lately hosted on a *.christmas domain? These posts seem to have nothing to do with the holiday I'll admit the only reason I've noticed the domain is my corporate IT seems to have all sites on weird TLDs blocked by default :/
- ldoughty 7y agoMy guess: seo
- nkrisc 7y agoApparently it's supposed to be some kind of advent calendar type thing, 25 posts in 25 days. Which explains the christmas tld.
- gherkinnn 7y agoExactly that. Bekk, a Norwegian agency, has built a few advent calendars themed around various aspects of programming and product development. There’s FP, JS, Kotlin, UX, etc
- deleted 7y ago[deleted]
- deleted 7y ago[deleted]
- sloonz 7y agocheck() .then(result => { if (result) { // set state to finished } check() .then(result => { if (result) { // set state to finished } check() .then(result => { if (result) { // set state to finished } check() .then(result => { if (result) { // set state to finished } check() .then(result => { if (result) { // set state to finished } // set state to not done }) .catch(error => // set state to failed); }) .catch(error => // set state to failed); }) .catch(error => // set state to failed); }) .catch(error => // set state to failed); }) .catch(error => // set state to failed); For the love of everything that’s sacred, please don’t do this. The strength of promises is that they can free us from that exact kind of callback hell, and they do that by being chainable. const resultOrNull = await Promise.resolve(null). then(result => result || check()). then(result => result || check()). then(result => result || check()). then(result => result || check()). then(result => result || check()). catch(error => null); Calling .then/.catch inside of a .then/.catch is a huge red flag. Almost always, you want to return the promise and chain instead.
- Pigo 7y agoCame here to comment on that example as well. I started panicking that someone might actually do that.
- gherkinnn 7y agoYou’re right of course. But you having the “.” dot one line above made my spine tingle in all the wrong ways. Not that it matters.
- sloonz 7y agoThe dot one line above is the better style. Change my mind. Pasting check() .then() in node REPL gives : > check() undefined > .then() Invalid REPL keyword Pasting check(). then() Gives the expected result. More generally, Javascript rules about whether a newline constitutes the end of a statement or not are pretty confusing (at least for me), so I prefer using a form that makes it explicit when a statement continues on the next line.