14 ms·
I Avoid Async/Await
- unityByFreedom 5y agoI'll take "blog posts asserting silly things for clicks" for $100, Alex. All of this author's examples using .then() are single-line functions. Seems contrived to suit their opinion. Just use the right tool for the job.
- pawelmurias 5y agoJust typical medium garbage with constructed examples to claim that their artificial view point isn't that stupid.
- BigJono 5y agoYou pretty much just described half the TC-39 proposals. I wish this shit was limited to noobs on Medium.
- cube00 5y agoIt's interesting using async/await, setting the target ES to be an older version and watching how the TypeScript compiler replaces async/await with yields and then no yields but a ton of scary looking boilerplate as you progress further back through older versions of ES.
- vbezhenar 5y agoAwait basically splits function in two functions (before and after) and uses second function as a callback for promise then. Error handling makes it a little bit more complex, but not much. Babel seems to make it harder than it needs to be, but may be that makes sense from performance or maintainability perspective.
- Aeolun 5y agoMany awaits -> many, many case statements.
- deleted 5y ago[deleted]
- mojuba 5y agoI know the article is about JavaScript, but I was wondering how Swift resolves the problem of weak self within the async/await model. If the part of the function after await is the callback, how do I specify weak self if I need to?
- ninkendo 5y agoYou can generally do something like: Task { [weak self] in await foo() self?.bar() // self may be nil } That is, you can just make a new task which awaits on something and then does more work, and that Task can be tagged with weak self so that self can be freed.
- mojuba 5y agoSo basically no way to do the same with await, other than of course declaring a weak local var.
- ninkendo 5y agoIf you’re await’ing a variable directly in the same scope, of course self will be retained, you’re still running inside self’s scope. I guess I’m confused by the question… if you want to await something and allow self to be freed in the process, you can use Task{ [weak self] } and then return early. If you don’t want to return early, you can’t really release self yet, since the scope shouldn’t outlive self.
- Go0the0gophers 5y agoSure async/await makes your casde more complicated . That reason why, I prefer use Go for I/O wait application because I don't need to manage this Go doesn't it fore me. My code still readable and simple.
- vips7L 5y agoUntil you need to do any error handling.
- zebracanevra 5y agoIf you're doing nothing in between your async calls, using .then/.catch might be simpler. As soon as you need to introduce local variables and complex control structures, not having shared closures between your .then methods becomes extremely limiting. Hence, the async/await sugar. About having to add await to ensure your error is handled with the try catch — not putting an await before a promise is something I use all the time. Being able to store tasks/promises and reuse/await them later is a clear advantage to other types of async code (such as coroutines).
- too_root 5y agoI agree. I think the article takes a simplistic view of the use cases in an effort to make the post consumable but in doing so, misses some of the point behind the syntax.
- Aeolun 5y ago> Being able to store tasks/promises and reuse/await them later is a clear advantage This sounds like horrible spaghetti. I can understand you might need to do it in exceptional circumstances, but I wouldn’t make a habit of it.
- zebracanevra 5y ago99% of the time you'll be storing promises in order to await on them in the same async method. Those other 1% of times make for quite intuitive solutions, I think. For example, a cache. Most caches are request => already finished response, but with a lookup of promises you can easily do request => in-progress responses, for de-deplication.
- yourad_io 5y agoGraceful shutdown & startup are two use cases that I used this for, just today. When my express server gets a SIGTERM I want it to stop accepting new requests but finish pending requests before exiting. Likewise during startup, the HTTP server is currently up before all resources are available - DB backends and the like. So I await a .ready promise before all requests iff we are not inited. It can be abused into spaghetti for sure, but actual use cases are not that exotic imho
- jokethrowaway 5y agoAsync / await is nice syntactic sugar. I don't understand why they copied the naming from c# over do syntax in Haskell (which dates roughly 1995, if I'm not mistaken). The main problem is error handling, having to use try / catch is pure cancer and really screw up your closure. You can use catch together with await / async but then you're not dealing with exceptions outside the promises.
- valenterry 5y agoasync/await is not the same as the do-notation, that's why.
- xigoi 5y agoHow is it not?
- valenterry 5y agoMany reasons, for example: 1. do notation works and is used for any monadic type (e.g. lists, parsers, promises, resources like database-connection-contexts, ...) while async/await works for promises only 2. async works on the function-level and only there while do-notation can be used in any place where a normal expression can be used, including being abitrarily nested. There are more differences, but that should be enough food for the mind to think about it. You could summarize it as: async/await makes it easier to work with promises on the syntax level and trying to make async code look like sync code, while the do-notation does not try to make async code to look like sync code, but rather embrace that sometimes two different semantical flows are interwined (one inside a monadic context, one pure) and makes it easier to work with them on the syntax level while at the same time making the two look _different_ explicitly.
- xigoi 5y agoI don't think we disagree. JavaScript should've gone with do notation precisely because it's a generalization of async/await.
- gombosg 5y agoAs a fullstack dev, I worked with - Spring MVC (Java Futures) - Spring Webflux (Reactor observables) - Scala (Scala Futures & for comprehensions) - TS async/await - Angular observables - React hooks (which combined with Redux, React Query etc. is a special approach to async programming.) I think it's very important to understand that ultimately, async (non-blocking) programming is kind of a "hard problem" and there is no trivial solution for it. But we can't get around it because we must not block code execution: Javascript runtime environments are single-threaded (except workers) and the JVM has limited thread count, too. Any senior developer has to understand how each of these approaches work and how they are trying to solve the issue of non-blocking code execution. I dare say that this problem area is one of the hardest ones during becoming a more senior developer. In terms of ergonomy (developer experience) when it comes to simple chained non-blocking calls, in my opinion nothing beats async/await in terms of syntax simplicity. I miss them from Java & Scala! No indentation, lambda functions, flatmaps, monads or special methods/hooks needed. You just have the result of an async method right there. I don't agree with the author that we shouldn't use async/await because it _looks like_ synchronous code. We _should_ use it to keep syntax simple and clean wherever possible! The developer has to properly learn the language fundamentals (and how JS code execution works) and know that async/await is just syntax sugar over Promises and generator functions. If they don't do this, then having a Promise.then() chain instead of async/await won't help that developer write better code. On the other hand, once they understand it, they can freely choose between async/await and Promises depending on what needs to be written.
- likeclockwork 5y agoYou can't talk about async await without talking about try, catch, and finally. I don't think async+await+try+catch+finally is simpler than promises in any way. I don't find anything simple or clean about imperative error handling.
- tm-guimaraes 5y agoTry ou kotlin coroutines. The suspend keyword really helps out reasoning.
- weinzierl 5y ago
- LAC-Tech 5y agoI kind of agree. I learned promises from the promise API - flatmapping over things, that will happen when the thing is resolved. I remember async/await was harder to grok because it seemed so arbitrary. A bunch of syntax sugar, and function decorations.. promises were just method calls.
- vhakulinen 5y agoRegarding the error handling case, I've played around with the idea of implementing a Result type with typescript and hiding the errors behind that. The end result looks something like this const res = await getJSON('/thingy'); if (isErr(res)) { // Handle error. return; } You can read the details from here: https://thingsthatkeepmeupatnight.dev/posts/simple-typescript-result-monad/ https://thingsthatkeepmeupatnight.dev/posts/simple-typescrip...
- wereHamster 5y agoCongrats, you've re-invented sumtypes!
- vhakulinen 5y agoRather, copied over from other languages.
- moron4hire 5y agoI try to read every one of these sorts of articles that comes up. I don't want to be caught flat-footed on some kind of weird memory leak issue from not using a pattern correctly, or something. But every single one of these articles that I've read against async/await has been nothing more than inventing "problems" to try to convince people to stick to raw promises strictly because that's what the author is comfortable with using. No actually technical merits are discussed. Just fantasy issues so author can feel superior for not learning something new. Like how the author claims you can't visually "see" that two await calls can be parallelized. Speak for yourself, buddy. And then complains about "having to go back to using promises" to affect the parallelization. My dude, it was promises all along. It's just syntax. Use it, don't use it, mix and match it. It's not a moral issue.
- gombosg 5y agoYup, I agree that the author is missing the point here. You can mix these syntaxes and have, say, "await Promise.all" without guilt.
- SideburnsOfDoom 5y ago> you can't visually "see" that two await calls can be parallelized. Speak for yourself, buddy. Agreed. My first thought was to try var userSaveTask = save('userData', userData); var sessionSaveTask = save('session', sessionPrefences); await Task.WhenAll(userSaveTask, sessionSaveTask); I saw it before I got to the part of the text explaining how I wasn't going to just see it. > Furthermore, if we are to take advantage of parallelization in the async/await example, we must use promises anyway. Again, Speak for yourself, buddy. "await Task.WhenAll" does that job in c#, and there must be a JavaScript equivalent. IDK, this may be the c# mindset. yes, "async / await" is an extra-ordinarily complex feature that can easily be done wrong. But its also very useful and powerful, so lets not throw it out.
- chrisco255 5y agoYes, it is `await Promise.all()` in JS. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- Aeolun 5y ago> It’s because we are taught to read async/await code in a synchronous mindset. We can’t parallelize the save calls in that first fully synchronous example, and that same — but now incorrect — logic follows us to the second example. What a weird idea. When I see a serial bunch of awaits the first I think of is whether that makes any sense. I think the author is kind of projecting his own views on everyone else here.
- lr4444lr 5y agoYOU may, buy your core frameworks may not. And if your synchronous code is holding up async loops, you're gonna potentially create a lot of very hard to diagnose problems. Beware the foundation on which you build.
- rwieruch 5y agoI always wondered why async/await introduced a concise way of dealing with asynchronous code, but then breaks all this conciseness with try/catch ... Hence I tried to combine async/await with catch for an improved readability [0] two years ago. Turns out I never used this approach, because it's not common sense when working in a team and still feels too verbose. Either one uses then/catch or async/await with try/catch. However, I still feel try/catch still makes code unattractive. [0] https://www.robinwieruch.de/javascript-async-await-without-try-catch/ https://www.robinwieruch.de/javascript-async-await-without-t...
- BigJono 5y agoI've never found this to be much of a problem in good code. API failures in front-end code tend to fall into a couple of buckets that can be abstracted away. If you want to preserve the page and keep retrying with exponential backoff, you can just implement that as a function that you then await until it succeeds. If you can't recover and just want to show the user a "shit's fucked, refresh the page" banner then you just need one handler near the top of the stack and let the errors bubble up. Most people are working in a declarative environment like React where you can just have a hook wrapping your API calls that exposes a potential error state to render accordingly. You only really need to have try/catch blocks in 1 or 2 places unless you have a lot of novel async stuff going on that needs to recover from failures in specific ways. Most front-end code shouldn't have a try/catch around every API call.
- adontz 5y agoI believe author misses one important point. Async/await optimizes for throughput, not latency. Use case is many lightweight tasks you want to parallel as wholes. Web APIs, web sites are usual examples. However, if you need other kind of optimizations, like two parallel saves within one request, maybe async/await is not what you need in the first place.
- jrochkind1 5y agoThe performance characteristics of promises and async/await are identical, no?
- chmod775 5y agoFirst off, async/await is promises. It's merely syntactic sugar. The point of async/await is not to never have the word "Promise" appear in your code. It's also not meant to be universally better than using Promises bare-bones. A lot of the argument appears to be the author extrapolating from his own lack of familiarity to others: "We are taught", "our minds", etc. I can easily construe some hypothetical person with a certain set of skills (and lack thereof) that would have just as much trouble with bare-bones Promises. But I don't have to because the author did it for me at "One more thing…". "People can mess this up" was never an argument. You can mess everything up. The more interesting question is how badly and how often.
- eurasiantiger 5y agoThis, very much this. async function() {} is much nicer than function() { return new Promise( (resolve) => resolve() ) }
- bin_bash 5y agoI prefer async/await but this is a contrived example. For starters you can just write: const myfunc = () => new Promise(resolve => resolve()) or Promise.resolve()
- wildrhythms 5y agoAs soon as you need to chain Promises, this gets very annoying and you quickly find yourself in a nested callback hell.
- bin_bash 5y agoIf you’re nesting “new Promise” you’re almost certainly doing it wrong. This is for generating new promises not executing them.
- Aeolun 5y agoconst myfunc = () => new Promise(resolve => resolve()) Is there anyone that actually likes this structure? I find it really hard to reason about what a line like this does. What is the upper limit on double arrows in one line?
- emilfihlman 5y agoPromises are the most awful programming syntax I have ever come across. Await is sweet release from death as nowadays almost all apis are forced onto promises. Thank god xhr and websockets came before promises were mainstream, but for example webserial is absolutely horrible to use. Function callbacks are the best, though I wish JS would support function scheduling.
- dimgl 5y agoWho actually thinks like this? Callbacks were absolutely unmaintainable. Promises (and `async/await`) are really not that hard.
- emilfihlman 5y agoUnmaintainable? Promises make the most horrible spaghetti code there is. It is hard to read, hard to maintain and hard to reason about. Callbacks are super simple and clear, both as an api and how data is passed.
- beebeepka 5y agoIt's funny you say this because I have been promisifying xhr for years until fetch came along. And these days I do the same when I want to simplify WebWorker communication. Promises are great
- atsjie 5y agoI never liked promises when they were first introduced because I never perceived this "callback hell" to be an unmanageable problem; there are excellent libraries to manage callback composition, e.g. https://caolan.github.io/async/v3/ https://caolan.github.io/async/v3/ When Promises came along I didn't feel they added much value; in fact it made things more complicated (new paradigm, harder to compose). Still, I went with it because everyone else went with it. Then async/await came along, I had the same issues as described in the article. Most issues in the article I've made manageable (learning patterns over time, using libraries and/or conventions). To me the article reads as someone who worked with async/await for some time and never wanted to embrace and work with them in the first place. I'll be honest; if I'm working in a team with a collegue trying to force all code to use promises instead of async/await I'd probably ask him to stop doing that and escalate depending on the response. Being idiomatic is very important, it makes code recognizable for newer devs. Had I stuck with callbacks until now I'd be writing code few young JS/TS devs would understand or like to work on.
- deleted 5y ago[deleted]
- jrochkind1 5y agoThe semantics of the library you link to seems fairly similar/analogous to promises to me! I'd in fact call promises themselves "an excellent library to manage callback composition", I don't see any reason to prefer the one you link over the promises api -- even if neither were built-in to JS. (And promises of course originally were not).
- atsjie 5y agoYour comment is a bit misleading, I doubt you looked for more than 30 seconds at that lib's documentation given the lack of detail in your response. Just to be clear; I would never use that lib in any circumstance right now, I'd always prefer async/await for idiomatic reasons. But please have a look at http://caolan.github.io/async/v3/docs.html#controlflow http://caolan.github.io/async/v3/docs.html#controlflow Forever, queue, series, times, retry, until, waterfall, whilst, etc etc... these are patterns that can all be achieved with promises too but I am sure most devs will do a google search before they do. In fact; some patterns are better served by a library. Only just yesterday I added https://www.npmjs.com/package/p-limit https://www.npmjs.com/package/p-limit to get concurrent promise limitation behaviour; one of the many features of that async-lib as well. Don't misinterpret this as me suggesting these features should be added to the standard; far from it. Community libs do this job quite well. I moved on from async, I moved on from Promises and embraced async/await. Do I like it? No, I much rather use Golang's channels. But async/await is idiomatic and plenty of libs out there to handle complex use cases. Almost everyone has moved on, and anyone writing promises at this moment is just creating legacy for anyone who is going to maintain it. Is that a good reason in itself? No, but it is a very valid one, that is my point.
- eurasiantiger 5y agoI stopped reading after the author made it clear they do not understand async/await is merely syntactic sugar for Promises.
- fithisux 5y agoI use it in python and I love it.
- codecurve 5y ago> Give better cues that we are in an asynchronous mental model The `async` keyword(!) is objectively a clearer signal that the code in question is asynchronous. That's why type-checkers use it to prevent you from doing dumb stuff like awaiting in a synchronous function. > In simple cases express the code at least as cleanly as async/await It's pretty hard to see much of an argument here. How can the promise version ever be seen as "at least as clean"? await someTask() // vs someTask().then(() => ...); Even beyond the syntax here, the promise forces you into at least one level deep of nesting, which instantly makes it much trickier to manage intermediate variables, which either need to be passed along as part of the result (usually resulting in an unwieldy blob of data) or the promises need to be nested (pyramid of doom) so that inner callbacks can access the results from outer promises. > Provides a much cleaner option for more complex workflows that include error handling and parallelisation. If you've somehow come to the conclusion that `Promise.all` doesn't work with `async/await` then you have probably misunderstood the relationship between `async` functions and promises. They're the same thing. Want to parallelise a bunch of `await` statements? You can still use `Promise.all`! I do occasionally find try-catch to be awkward, but that's because it creates a new lexical scope (just like promise callbacks do). I also think the consistency from having one unified way to handle errors in sync/async contexts justifies it.
- BillyTheKing 5y agothe whole blog-post seems a little constructed tbh - If I look at await doSomething(param) await doSomethingElse(param) the very first thing that comes to mind is that this can be very, very easily optimised by just doing await Promise.all([ doSomething(param), doSomethingElse(param) ]) This is something I come across literally every day - why exactly would that be an argument against async/await? much so on the contrary, I find const myResult = await Promise.all([]) significantly more expressive, than Promise.all([]).then(doSomething) Also, as others have already pointed out - async/await is just syntactic sugar around promises and can be used in addition to 'normal' promise syntax (as shown above)
- johnny22 5y agoOnce you're sure the operation doesn't need to happen serially I hope :)
- manigandham 5y agoAll async/await is built on promises/futures/tasks, which themselves are built on yield/generators. Most modern languages have converged on this and they are not different paradigms but rather just newer syntax. Async/await is better for expressing concurrent logic more tersely and in the common "sync" format, and failure to understand what it means is just that, a failure in understanding. There's no convincing argument here to use "promises instead async" because it's the same thing.
- zazibar 5y agoNow maybe it’s just my familiarity with Promises, but I look at the third example and I can quickly see an opportunity. This entire article is built around the author's ignorance and could easily be summarised as "I avoid async/await syntax because I'm more familiar with promises". The author doesn't even appear to understand that async/await is syntactic sugar for promises.
- codeflo 5y agoThe author even implies in a footnote that switch statements are unusable. I mean, we probably all had painful experience with the “gotcha”, and I appreciate efforts towards safer designs. But I mean, let’s not be ridiculous. It works fine.
- sidewndr46 5y agoI did not take me long to reach the same conclusion. The article can be summarized as "I am ignorant of the meaning of async/await, thus I don't use it". This is perhaps one incremental improvement from "I am ignorant of async/await, thus I use it poorly". But in the wrong direction.
- jrwr 5y agoI avoid Javascript outright because async/await/promise is confusing to me. I blame it on being a PHP Programmer and likes things to run serially.
- farmin 5y agoHyperscript can help with this. https://hyperscript.org/ https://hyperscript.org/ Makes using a bit of JavaScript relatively simple, just not much in Stack Exchange yet which means reading docs..
- gorjusborg 5y agoI felt the same way coming from a threaded language. Learning the event loop, then promises, then async/await is a must. Today, you probably should throw typescript on top. A steep learning curve just to get back to a typed language that can do things concurrently. You do get used to it, but it is a mess of stuff.
- __ryan__ 5y agoThis article takes serious mental gymnastics to follow. The main argument seems to be that an engineer with a working mental model for basic promise chaining will be unable to translate that to async/await, which is effectively syntax sugar for the same thing. The author says that multiple calls to <promise>.then() is a cue that the code is occurring serially, and the new keyword await <promise> somehow isn’t. There’s a time and place for raw promises, but this article hardly touches on anything actually wrong with async/await. I’ve worked with engineers who actively fight against learning their tools, like this. It’s a nightmare. I don’t trust them. Don’t be that person.
- kareemsabri 5y agoThe premise of this article seems flawed. We don't "code in a synchronous mindset" when we use async/await. I would expect most programmers are aware, after a 5 minute google search, that there is an asynchronous process happening and we need to ensure no execution after that line occurs until that process has resolved, hence we use await. If you know what it is doing, it's not complicated at all. It's identical to promise chaining but less ugly.
- daxfohl 5y agoUltimately the problem of potentially missed opportunities for parallelism there in regular sync code too. If we take the author's point seriously, we should all be programming in a language that requires explicit continuations everywhere just to guard against accidentally serializing things.
- pausmfib 5y agoI understand that the author is more familiar with the syntax of promises. Nonetheless, having a syntax similar to synchronous code allows developers to write more code remembering only the couple of changes in syntax instead of having to memorise a different one.
- stupidcar 5y ago> The keywords async and await should give us the same intuition that the then does in the third. But I’ll wager for many engineers it doesn’t. It does.
- AWebOfBrown 5y agoAgree that try/catch is verbose and not terribly ergonomic, but my solution has been to treat errors as values rather than exceptions, by default. It's much less painful to achieve this if you use a library with an implementation of a Result type, which I admit is a bit of a nuisance workaround, but worth it. I've recently been using: https://github.com/swan-io/boxed https://github.com/swan-io/boxed. By far the greatest benefit is being able to sanely implement a type-safe API. To me, it is utter madness throwing custom extensions of the Error class arbitrarily deep in the call-stack, and then having a catch handler somewhere up the top hoping that each error case is matched and correctly translated to the intended http response (at least this seems to be a common alternative).
- qudat 5y agoI was really hoping this article would discuss the benefits of using raw generators over async/await. By using async/await, we are inherently limited by the flow control because we are forced into await resolving promises. This is why libraries like redux-saga[0] or cofx[1] use generators. https://redux-saga.js.org https://redux-saga.js.org https://github.com/neurosnap/cofx https://github.com/neurosnap/cofx Generators provide much better flexibility over flow control and allow us to treat side effects as data.
- anonymousDan 5y agoI don't find the first example convincing at all - to me await clearly indicates you are going to be blocking until the result arrives.
- einpoklum 5y agoI like big promises and I cannot lie?
- jdrc 5y agoWe need an AI to design a language that is optimal for humans to program with. Languages should be designed by behavioral scientists rather that computer scientists bcause their goal is to adapt to the human rather than the machine. For example one of the biggest limitations of humans is limited capacity of working memory. Async paradigms exhaust this limit very early and lead to frustration compared to serial programming. Writing one of them in the form of the other doesn't solve any problem and is indeed a travesty
- jrochkind1 5y agoIn that first example, `await` means exactly the same thing as `then` for the purposes the author is talking about -- realizing that something had been serialized that could have been done in parallel. I don't see why most engineers would be likely to recognize that opportunity when written as `then` but not as `await`. If is is true (and I'm not convinced it is), it seems like an education problem rather than a reason to use promises rather than await. It doesn't seem fundamentally harder to know that two `awaits` in a row means serialization than to know that two `thens` in a row does. At least in that simple example where the use of `then` is exactly parallel to the use of `await`. Perhaps it wasn't a good example, and it really would be harder to reason about in a more complicated example.
- steve_taylor 5y agoI’d like to see the author rewrite a for-of loop that contains async code in the body as traditional promise code, then look me in the eye and tell me the for-of version is more awkward.
- recursivedoubts 5y agoI work on a web scripting language that abstracts away asyc/await: https://hyperscript.org/docs/#async https://hyperscript.org/docs/#async we call it "Async Transparency" It lets you write code like this: fetch /some-url as json put the result's content into me where fetch is an async call, but you don't have to await it or anything or mark it as being an async fuction, whathaveyou effectively, we de-color the language: https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ https://journal.stuffwithstuff.com/2015/02/01/what-color-is-... my feeling is that async concerns are too low level for light scripting
- zkldi 5y agoHow would you allow running the fetches in fetch /some-url as json fetch /some-other-url as json do_stuff_with(result1, result2) in parallel?
- recursivedoubts 5y agohyperscript is focused on the common cases for light front end scripting, so right now there isn't syntax for doing operations in parallel. If I needed that I would kick out to javascript and use Promise.all() to return an expression that hyperscript could then sync on the wheelhouse for hyperscript is stuff like: on load wait 5s transition my opacity to 0 remove me where you don't have to do any async or callback stuff for what are, at root, async operations.
- zkldi 5y agoI don't get it then; your fix to promises being complicated is to remove the ability to perform asynchronous actions. That's cool for a small scripting language I guess, but absolutely impractical for anything serious.
- recursivedoubts 5y agohyperscript is designed for small, embedded front end scripting needs: toggling classes, listening for events from its sister project, https://htmx.org https://htmx.org, etc. It isn't a general purpose programming language for use, for example, on the server side in a node-like environment. you can still perform things asynchronously by wrapping any expression or command in an `async` prefix: https://hyperscript.org/commands/async/ https://hyperscript.org/commands/async/ but there isn't a mechanism for resolving all of them although, now, come to think of it, the following would work: set results to {result1: somethingThatReturnsAPromise(), result2: somethingElseThatReturnsAPromise()} That would work out because under the covers the hyperscript runtime calls a Promise.all() on those field values before it continues. Kind of a hack, but it would work. Anyway, again, hyperscript is a DSL targeted at small front end scripting needs rather than being a large scale concurrent systems programming language.
- namelosw 5y agoAsync/await to Promise in JavaScript is just do notation to monads in Haskell, what's the problem?
- robmccoll 5y agoEverytime I use a language with async / await syntax, I find myself wishing that awaiting async functions was the default behavior and that I had to specify when I didn't want that behavior. So much boilerplate, so many times I've seen bugs that are the result of not being aware of a function being asynchronous or that the function was later made asynchronous. Most of the time you want seemingly synchronous behavior within a request because it's easier to read, write, understand, and reason about and concurrent progress across requests. The places where I can overlap I/O within a request can be manually specified. I guess this is kind of how Go works (although that's parallelism and concurrency).
- deleted 5y ago[deleted]
- davidmurdoch 5y agoI like this idea!
- chmod775 5y agoYou might be interested in these linter rules: https://typescript-eslint.io/rules/no-misused-promises/ https://typescript-eslint.io/rules/no-misused-promises/ https://typescript-eslint.io/rules/no-floating-promises/ https://typescript-eslint.io/rules/no-floating-promises/ They can catch some common mistakes. Of course these checks can only work reliably when you're using a typed language. I'm still looking for one that forces me to put an await in front of every function call that returns a promise, unless I opt out.
- audunw 5y agoYou may want to check out Zig's async/await. Seems to be closer to what you're suggesting. It's one of the few implementations where functions don't have an "async"/"no-async" color, and where you use the same IO functions regardless of whether you want to use them asynchronously or synchronously (if I remember correctly).
- oaiey 5y agoI actually start to appreciate function coloring in C# to never forget that I am operating on asynchronous code. In .NET the compiler also throws warnings if you do not await or pickup the task/promise. I also believe that you should see where asynchronous behavior can happen in your code. So from that angle i also appreciate the keyword and the coloring. Hidden resource access is pure evil.
- AshleysBrain 5y agoTheir promise-based error handling seems to skip over a major gotcha: if the first `save` call throws an exception, it does not get handled in the `.catch()` callback! It would require its own try-catch to handle. That is an overlooked benefit of try-catch in async functions: it handles everything, both normal exceptions and promise rejections.
- Izkata 5y agoA rejected promise is passed along until it hits a catch. Both save calls are handled by the one there.
- divan 5y agoAuthor mentions that async/await messes up with mental model of the code, and I think that's the most important issue with it, but it goes even deeper than described in the article. There are no inherently async or sync functions. It's not a property of a function, rather the property of what caller does after calling a function. Is throwing a ball an async or sync action? Well, if tennis robot machine spits one ball after another and doesn't care/wait about feedback – then it's async. If the tennis player hits the ball with a racket and puts all her/his attention into waiting/validating the result (essentially blocking) – then it's synchronous function. It's essentially an "attention" of the caller that defines sync or async. Marking code as inherently "sync" or "async" or claiming that one functions are "hard" and others are "easy" is the single dumbest idea I've seen in computer programming. And it's insane how contagious it is - seems like languages are more often copypasting features from other, instead of designing from the first principles.
- jffry 5y agoSince this article is specifically about Javascript: I think the implementation of async/await in the language is heavily constrained by a desire for backwards compatibility. After all, async/await is ultimately promises all the way down, so you can happily write code and let a compiler turn it into ES5 that runs anywhere. If instead JS allowed you to shunt arbitrary function calls off into their own threads, then you would need bigger changes to the engines to support that, and you couldn't back-port that to vanilla javascript.
- divan 5y agoTo be honest, I find promises even worse concept for concurrent programming. At least I never think about things and behaviour in terms of "objects that will yield value in the future". To use promises I need to build a layer of conversion between "how my brain thinks about world" and "passing around promises" – and that's just textbook accidental complexity and unneeded cognitive load. The least cognitively expensive model for concurrent programming is CSP, precisely because it fits into how we humans reason about world.
- 5y ago
- todd3834 5y agoVery strange to compare async/await to promises when it is just a part of promises. There are rare situations where promise callbacks might be easier to read but those are more edge cases and a matter of personal preference.
- codeflo 5y agoSome language features aren’t aimed at toy examples, and if that’s the scope of your thinking, you won’t see the point. For example, I like how the catch block is just a single function call to handleErrorSomehow — which would be totally fine for an example, if it weren’t for the fact that the author makes a special note of how convenient it is that the Promise variant reduces to just .catch(handleErrorSomehow). Suuuper-realistic. I should probably admit that I also didn’t completely see the point of async/await — this was years ago in C# — until I first had a chance to use it inside a complex loop of some kind. I think it’s a good exercise to try manually desugaring such an example. It really makes you appreciate what the compiler’s doing for you in these cases.
- forty 5y agoI don't mind using async/await when I use promises, but in my experience people who never worked with bare promises have a hard time really understanding async/await and will eventually need help from people who did for harder problems. Just like people who never used callback flows often struggle to understand well asynchronicity and mess up with promises.
- andix 5y agoI always do await Promise.all(…) And now?
- darepublic 5y agoThis take is garbage imo. Coming from someone who first used jquery deferred, then native promises and now async await. If you are too inexperienced to use await properly no reason to think callback hell is going to make it better
- omgbear 5y agoFor me, the key to async/await is a linter that complains about un-handled promises. I appreciate in Go that any green threads are created explicitly with `go`, whereas in typescript, they are signaled by the return type. My most recent background was in Go and so my mental model now is that async functions are similar to goroutines and `await` is a nice result collection mechanism. What's recently confused me is that exceptions from yet-to-be-awaited promises crash if you await anything else first.
- bsuvc 5y ago> I Avoid Async/Await Tell me you're a difficult teammate without saying you're a difficult teammate.
- rootusrootus 5y agoEven though I feel competent enough using them, I continue to dislike promises in any form, no matter how much syntactic sugar there is.
- beebeepka 5y agoI wonder what do you do instead.
- eyelidlessness 5y agoLong rant is long, so I’ll leave a tl;dr: regardless of how you feel about the syntactic sugar aspect, async/await has a runtime advantage of suspending the stack rather than exiting early, which is (IMO) of greater benefit than the syntactic improvement. - - - I’m in the process of gradually moving several decade-old JS projects from explicit Promise APIs to async/await. The reason for this is not the syntactic sugar (although that’s quite a benefit, and I disagree wholeheartedly with the article on that point). The reason is that, at least with native Promises, the actual runtime behavior of async/await is considerably better. Because await suspends the stack, where .then/.catch enter a new stack frame. I do see this has been discussed some in another thread, but I feel like it deserves more direct attention. It’s true that you can use libraries like Bluebird or whatever to get more accurate stack traces. But this is done by tracking Promise chains in user space. This is fine but it comes with a cost. Even if the library is quite fast (as Bluebird is), you still incur the cost of downloading and parsing it. Especially on HN where JS bloat is a daily topic, I’d hope this can be appreciated. In my case, this cost wouldn’t be worth it even if the Promise API were clearly better. These projects need to run efficiently on low power mobile devices with limited connectivity. And we need to be able to diagnose runtime errors to address outstanding bugs. A library is a non-starter, and native Promise APIs actively hinder progress. The behavior of async/await is clearly superior. Suspending the stack, either with async/await or generators, affords significantly better debugging capabilities. It allows Error stacks and console.trace to take better advantage of source maps. It‘s also a much better optimization target for browsers. This isn’t just academic: even if async/await is semantically equivalent to Promise, it’s not a guarantee that the underlying .then/.catch will even be called if there’s no explicit Promise in the code. I’ve seen cases where they’re not, which was baffling until I understood (and this is how I came to understand) the difference in runtime behavior.
- whatdidiexpect 5y ago
- whatdidiexpect2 5y ago
- tobyhinloopen 5y agoI avoid Javascript. Not because Javascript sucks, but most libraries for Javascript suck.
- biglost 5y agoMaybe im wrong but i think await is not for everything it’s another tool, we as developer must known these tools, and when its better to use it or no
- deleted 5y ago[deleted]
- STRML 5y ago> a try block in JavaScript immediately opts that section of code out of many engine optimizations as the code can no longer be broken down into determinative pieces This isn't true and the dependent clause doesn't even make sense. Yes, V8 had trouble optimizing try/catch back in the Crankshaft days. But those days are long gone, and major engines (V8/TurboFan, JSC, and Mozilla's latest *Monkey) handle this construct quite well. Even if this were not true, it would be meaningless, as this is simply syntactic sugar over `Promise#catch()`, which you would be using anyway.
- imbnwa 5y agoAsync/Await covers the 80% of use cases for async logic in JS. Most people aren't really using promises as multicast references. They don't call `then` in one place, hang on to the promise reference, then call `then` again somewhere else (perhaps to represent a cached value); they call `then` once on the reference because it's just a moment in a composite operation. It's for this reason that I think this library[0] is the more appropriate abstraction for that same 80% of use-cases, as its more memory efficient since you can represent the same composite operation that generates multiple promise references with a single object (a unicast reference instead). I haven't learned Rust but apparently the author bases this on Rust's ownership principle. [0]https://github.com/mitranim/posterus https://github.com/mitranim/posterus
- z3t4 5y agoI understand there are two sides/groups here: 1) Those who think errors are important to control flow, and 2) Those who think errors are exceptions to control flow. If you are in group 2 both Promises and async/await will give you neat and simple code. But if you are in group 1 Promises and async/await will be really complicated and ugly because each await will be inside a try/catch. Because I'm in group 1, I try to avoid Promises (and thus async/await) in JS because the promise will capture all future errors, and if you forget to add a .catch, that error will never surface. Promises make asynchronous code more complicated. Async/await however get rid of a lot of the complicated syntax, so when I do not care about errors (eg. errors are exceptions) I use async/await because of easier control flow.
- z3t4 5y agoThose who think of errors as exceptions can further be divided into group 2a) Those who prefer Promises syntax. And group 2b) Those who prefer async/await syntax. Also don't forget about co-routines: co(function* () { var user = yield getUser(); var comments = yield getComments(user); }); Just replace "co(" with async, and yield with await, and you'll have async/await.
- jseban 5y agoErrors are an important part of the control flow, if you are doing UI programming, which is what Javascript is made for.
- valcron1000 5y agoSo "do notation considered harmful"?. Too bad that languages like Haskell are not very popular. These kind of discussions feel pointless.
- timcavel 5y agoThis confuses async with parallelism, probably because JavaScript is his threading model.
- justsomeuser 5y agoI don’t think of async await as “making async code act like sync code”. I see it as adding meta data to a function type (async) and allowing you to attach the result to a given function scope (await). The key utility of this is to allow you to use functions as a unit of composition, allowing you to leverage their already built tooling (IDE jump to function, stack frame based debugging, input and output types (in:args -> out:return). Messaging runtimes like Golang and Erlang use an additional composition unit (channels and mailboxes), and do not allow you to follow the “tree of functions” paradigm like async await does. These result in having to follow a network graph of messages and nodes to find out what will happen. You need to understand you are dealing with an event loop before you use async await. I think this is the issue the author is raising.
- phendrenad2 5y agoAsync/await was the latest buzzword a few years back, but it never had any substance. A lot of inexperienced programmers thought it was the greatest thing ever. Unfortunately, they were overly focusing on the look of the code, and not the true readability or usability of the code. Async/await certainly makes you code "look" synchronous, but it's just shuffling the complexity around. The complexity is still there, and you will have to think about it.
- parentheses 5y agoJavaScript Promises have their issues - the same ones all asynchrony-providing libraries do. I feel the author takes a hard stance on something quite nuanced. Syntactic sugar hides the API, making it more ergonomic, also leaking those problems. Same problem, different API
- Nathanba 5y agoBut the async/await/Promises only make the code look synchronous because it actually makes the code synchronous. The await literally blocks the current thread, it's nice that the code is executed in a thread scheduler but I'm still blocking the current thread. How this was accepted as a big win is not clear to me. If you want to make it asynchronous you still have to write .Then(()=> {}) which is a nested function. So we won nothing on that front. It's literally just about taking tiny pieces of code out and running them in a different thread... while the current thread is blocked anyway. So all this hurrah for almost nothing gained. In C# we already had a task scheduler ala Task.Run(()=>{}). Async instead of Task.Run() seems like such a microsopic win in exchange for an enormous amount of complexity.
- ttymck 5y ago> The await literally blocks the current thread I thought "async io" was supposed to be superior to "blocking the thread". The unit of computation is "smaller" than an os thread, right? It's cooperative multitasking, where thread-per-task is not cooperative. Maybe you were using "thread" symbolically?