All functions, even non-async functions, are colored. In any large system codebase you'll have functions that can only be called in certain situations, with the right setup, whatever, and if you're lucky this is communicated by types but regardless those restrictions can't be avoided. It's easy to call low-restriction functions from high-restriction ones and not the other way around.
Furthermore, it's not like the alternative to explicit await doesn't have issues too (that the article doesn't mention). There is inherent complexity, it's a tradeoff, you can't just syntax it away.
Author makes up a lie.
Then lampshades it away with a colorful non sequitur.
---
The alternatives that people praise like golang, have other tradeoffs that are much worse because the async logic is now implicit. Your entire codebase is now a surface area that is at risk of being blocked by waiting on a channel; the the mitigation of this is through responsible use of coroutines, but then you're right back around to extra information about your code that is analogous to colring, except not as explicit as async/await.
That makes it a pleasure to code concurrent stuff for IMHO.
It does have its own similar problems though - does a function return an error? If so you are going to need to plumb the error return through all the callers. Does a function need a context.Context? Ditto.
I guess you can't win them all :-)
Like, why can't my sync function await something asynchronous? If it has to lock up the whole thread while that function executes, that's fine because that's how it was going to work anyway 99% of the time
Related, one of the former React maintainers wrote a primer on algebraic effects that's a good read: https://overreacted.io/algebraic-effects-for-the-rest-of-us/
* Haskell: pure function and non-pure (IO monads) looks different. * Rust: unsafe functions (or block) requires special markers.
This comes at a cost, namely that of reading five extra characters in a function signature, and I could kind of imagine (truly!) how that gets in the way for some people. There is a cost of writing the five characters as well (and like the author mentions, in a poorly designed codebase, this may have to go down the call stack), but code is read more often than written, so in a sense this is negligible.
Like the dynamic vs static typing debate, I feel like this ultimately boils down to context and personal taste, and some amount of intelligence as well. I'm impressed by the amount of stuff the dynamic typing / non-async crowd is able to keep in their working or long term memory while coding. I don't have that kind of mental bandwidth, sadly.
Having said all that, this argument is disingenuous in that it completely ignores the fact that the async keyword tells you something useful (rather than some made up nonsense like color), and most of the argument basically boils down to "if you ignore the benefits, this syntax has no benefits", and I really don't respect that as an argument.
Thanks for my next horror shortfilm plot. Twist: he's the protagonist