First 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.
async/await is _safer_ too, because part of that syntactic sugar is wiring up exceptions correctly and ensuring that a function only returns or rejects asynchronously and never synchronously. This prevents lots of bugs.
> First 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
Sounds like you didn't understand the point of the article. Unfortunately, you're arguing against something the author never said, the author never said that async/await weren't promises. You just got stuck on the fact that the author used the term "promise" for the non-async/await style of code. I understood what the author meant, anyway.
> "People can mess this up" was never an argument
Unfortunately, you're arguing against something the author never said here, also. The author is saying that non-async/await code is better code than async/await. Not that "people can mess this up" or something.
> The point of async/await is not to never have the word "Promise" appear in your code.
Indeed it is not the point, and in that sense your criticism of the article is correct.
However, there are people who argue[1] that it is, in fact, a good thing to never have the word "Promise" appear in your code, that not having the freedom to execute whatever code you want after launching an asynchronous operation and instead having the predictability of always returning to the scheduler makes reasoning about asynchronous code much simpler. (For one thing, you can now reliably think—and have syntactic support for thinking—in terms of sequential coroutines with predictable yield points and known parents.)
In the context that Smith refers to (trio vs asyncio in Python, regarding which also see the first, more Python-specific but IMO better-argued post[2]) my experience actually bears that out. How well this works given the historical API baggage in JavaScript, I don’t know, but probably not very well.
Another curious thing is that the line of research JavaScript promises originate from[3] (Mark Miller co-wrote the spec proposal IIRC) does not use them as the user interface; instead, using the "eventual send"[4], you queue up method calls to objects (which can be promised results of other queued calls, and you may pass other promised objects as arguments).
I cannot clearly articulate the relation of this to Smith’s notion of “structured concurrency” (I wish I could), but in any case it contributes some more weight to the opinion that the ergonomics of Promises are indeed subpar. It might well be, though, that in the context of JavaScript you can’t actually enforce enough structure on the preexisting APIs to build a superior alternative.
Now 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.
If 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).
This 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.
- 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.
I think I understand you point when it comes to the essentially single threaded Javascript runtime, but the thread limit of the JVM is huge, so how does that matter?
Not trying to be pedantic, just wanted to know if I missed something.
You 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.
I 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/
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.
The 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).
Same here, I've never had that callback hell problem either, I think it's pretty elegant actually and you can do a lot of nice refactoring by extracting callbacks into variables. And the big strength is that your programming actually looks the way that the runtime works. It just becomes confusing with another abstraction that obfuscates the way the runtime works and requires you to now have this mental translation model.
Everytime 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).
You 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).
Kotlin coroutines do something like this. Any calls to a suspend function from inside another suspend function will be a suspension point and does not need additional syntax. And typically, if you are writing Kotlin using a JetBrains IDE the IDE will have a little indicator in the gutter to show all the suspend function calls.
To me personally it is much more readable with the gutter indicators instead of additional await keywords inside the actual code but it has a drawback: you need an IDE that supports it. When reviewing code on gitlab/github there won't be any indicator.
Heh I suggested this once on HN and got modded down to hell. I completely agree. I think any performance advantages of the async model are lost because it adds so much complexity to just reading and following code.
The amount of effort JS programmers have to go to in order to write synchronous code that is legible, works and easy to understand is enormous. I am a Bad Programmer so accept that some of it is just my lack of willingness to spend the time to get over that hump but I find it almost impossible to believe the gains are worth the costs.
I 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.
The 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.
> 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.
> 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.
Regarding 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;
}
I 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.
I'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.
I 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.
> 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.
> Like how the author claims you can't visually "see" that two await calls can be parallelized.
I'm not gonna pretend to be a JS expert here, but isn't that exactly what you can see? Isn't that the whole point? It lets you easily take a second look and decide to check if the operations can happen in parallel or not.
I'm literally using 2 awaits in a row because android's BLE implementation doesn't necessarily like more than 1 operation happening at once, and I'm not doing enough other operations such that I need to implement a whole queue to handle it.
(although maybe the library i'm using should implement that queue)
In 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.
Their 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.
Author 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.
Since 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.
Agree 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.
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).
[+] [-] chmod775|3 years ago|reply
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.
[+] [-] gotaquestion|3 years ago|reply
This is literally 97.5% of all tech blogs, and a disappointing amount of content on HN front page.
I feel bad for the author. Nothing like asserting your ignorance on a blog. There's a time and a place for literally every language construct.
[+] [-] Waterluvian|3 years ago|reply
[+] [-] spankalee|3 years ago|reply
This article is bad, bad advice.
[+] [-] eurasiantiger|3 years ago|reply
[+] [-] phendrenad2|3 years ago|reply
Sounds like you didn't understand the point of the article. Unfortunately, you're arguing against something the author never said, the author never said that async/await weren't promises. You just got stuck on the fact that the author used the term "promise" for the non-async/await style of code. I understood what the author meant, anyway.
> "People can mess this up" was never an argument
Unfortunately, you're arguing against something the author never said here, also. The author is saying that non-async/await code is better code than async/await. Not that "people can mess this up" or something.
[+] [-] mananaysiempre|3 years ago|reply
Indeed it is not the point, and in that sense your criticism of the article is correct.
However, there are people who argue[1] that it is, in fact, a good thing to never have the word "Promise" appear in your code, that not having the freedom to execute whatever code you want after launching an asynchronous operation and instead having the predictability of always returning to the scheduler makes reasoning about asynchronous code much simpler. (For one thing, you can now reliably think—and have syntactic support for thinking—in terms of sequential coroutines with predictable yield points and known parents.)
In the context that Smith refers to (trio vs asyncio in Python, regarding which also see the first, more Python-specific but IMO better-argued post[2]) my experience actually bears that out. How well this works given the historical API baggage in JavaScript, I don’t know, but probably not very well.
Another curious thing is that the line of research JavaScript promises originate from[3] (Mark Miller co-wrote the spec proposal IIRC) does not use them as the user interface; instead, using the "eventual send"[4], you queue up method calls to objects (which can be promised results of other queued calls, and you may pass other promised objects as arguments).
I cannot clearly articulate the relation of this to Smith’s notion of “structured concurrency” (I wish I could), but in any case it contributes some more weight to the opinion that the ergonomics of Promises are indeed subpar. It might well be, though, that in the context of JavaScript you can’t actually enforce enough structure on the preexisting APIs to build a superior alternative.
[1] https://vorpus.org/blog/notes-on-structured-concurrency-or-g...
[2] https://vorpus.org/blog/some-thoughts-on-asynchronous-api-de...
[3] http://erights.org/talks/thesis/index.html
[4] http://erights.org/elib/distrib/pipeline.html
[+] [-] zazibar|3 years ago|reply
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.
[+] [-] zebracanevra|3 years ago|reply
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).
[+] [-] __ryan__|3 years ago|reply
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.
[+] [-] gombosg|3 years ago|reply
- 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.
[+] [-] weinzierl|3 years ago|reply
I think I understand you point when it comes to the essentially single threaded Javascript runtime, but the thread limit of the JVM is huge, so how does that matter?
Not trying to be pedantic, just wanted to know if I missed something.
[+] [-] mpweiher|3 years ago|reply
Not having code that is actively deceptive sounds like a good idea to me, so agree with the author.
> We _should_ use it to keep syntax simple and clean wherever possible!
Having clean and simple syntax for async operations is a laudable goal.
Having code that is actively deceptive about its semantics seems less than ideal for accomplishing this goal.
Maybe we need to figure out new and different syntactic constructs that are clean and simple, but do not pretend to be something they are not?
[+] [-] tm-guimaraes|3 years ago|reply
[+] [-] likeclockwork|3 years ago|reply
I don't find anything simple or clean about imperative error handling.
[+] [-] atsjie|3 years ago|reply
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.
[+] [-] jrochkind1|3 years ago|reply
[+] [-] jseban|3 years ago|reply
[+] [-] unknown|3 years ago|reply
[deleted]
[+] [-] robmccoll|3 years ago|reply
[+] [-] audunw|3 years ago|reply
[+] [-] zorr|3 years ago|reply
To me personally it is much more readable with the gutter indicators instead of additional await keywords inside the actual code but it has a drawback: you need an IDE that supports it. When reviewing code on gitlab/github there won't be any indicator.
[+] [-] chmod775|3 years ago|reply
https://typescript-eslint.io/rules/no-misused-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.
[+] [-] trog|3 years ago|reply
The amount of effort JS programmers have to go to in order to write synchronous code that is legible, works and easy to understand is enormous. I am a Bad Programmer so accept that some of it is just my lack of willingness to spend the time to get over that hump but I find it almost impossible to believe the gains are worth the costs.
[+] [-] oaiey|3 years ago|reply
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.
[+] [-] unknown|3 years ago|reply
[deleted]
[+] [-] davidmurdoch|3 years ago|reply
[+] [-] unityByFreedom|3 years ago|reply
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|3 years ago|reply
[+] [-] kareemsabri|3 years ago|reply
[+] [-] Aeolun|3 years ago|reply
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.
[+] [-] codecurve|3 years ago|reply
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"?
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.
[+] [-] vhakulinen|3 years ago|reply
[+] [-] wereHamster|3 years ago|reply
[+] [-] rwieruch|3 years ago|reply
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-t...
[+] [-] BigJono|3 years ago|reply
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.
[+] [-] moron4hire|3 years ago|reply
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.
[+] [-] SideburnsOfDoom|3 years ago|reply
Agreed. My first thought was to try
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.
[+] [-] johnny22|3 years ago|reply
I'm not gonna pretend to be a JS expert here, but isn't that exactly what you can see? Isn't that the whole point? It lets you easily take a second look and decide to check if the operations can happen in parallel or not.
I'm literally using 2 awaits in a row because android's BLE implementation doesn't necessarily like more than 1 operation happening at once, and I'm not doing enough other operations such that I need to implement a whole queue to handle it.
(although maybe the library i'm using should implement that queue)
[+] [-] gombosg|3 years ago|reply
[+] [-] jrochkind1|3 years ago|reply
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.
[+] [-] bsuvc|3 years ago|reply
Tell me you're a difficult teammate without saying you're a difficult teammate.
[+] [-] AshleysBrain|3 years ago|reply
[+] [-] Izkata|3 years ago|reply
[+] [-] divan|3 years ago|reply
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|3 years ago|reply
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.
[+] [-] AWebOfBrown|3 years ago|reply
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).
[+] [-] recursivedoubts|3 years ago|reply
https://hyperscript.org/docs/#async
we call it "Async Transparency"
It lets you write code like this:
where fetch is an async call, but you don't have to await it or anything or mark it as being an async fuction, whathaveyoueffectively, we de-color the language:
https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...
my feeling is that async concerns are too low level for light scripting