> If anything, async-await feels like an extremely non-functional thing to begin with
Futures/promises (they mean different things in different languages), like many other things, form monads. In fact async-await is a specialization of various monad syntactic sugars that try to eliminate long callback chains that commonly affect many different sorts of monads.
Hence things like Haskell's do-notation are direct precursors to async-await (some libraries such as Scala's monadless https://github.com/monadless/monadless make it even more explicit, there lift and unlift are exactly generalized versions of async and await).
To see how async-await might be generalized, one could turn to various other specializations of the same syntax, e.g. an async that denotes a random variable and an await that draws once from the random variable.
To see the correspondence with a flatMap method (which is the main component of a monad), it's enough to look at the equivalent callback-heavy code and see that it looks something like
I'm not clear on if this is supposed to be disagreement or elaboration or education.
The fact that in a language like Haskell, you can perform something like async-await with futures (which are absolutely a kind of monad) in a natural way is precisely what I had in mind with what you quoted.
Regardless, the specific heritage of async-await syntax seems rooted in procedural languages (that do borrow much else as well from functional languages, yet are still not functional in any meaningful sense) like C# and python. They are absolutely an attempt to bring some of the power of something like monadic application (including do notation) into a procedural environment as an alternative to threads (green or otherwise), which hide the execution state machine completely.
> the specific heritage of async-await syntax seems rooted in procedural languages
I don't think so. Async-await in both syntax and semantics is pretty firmly rooted in the FP tradition.
For semantics, the original implementation in C#, and as far as I know most of its successors, is to take imperative-looking code and transform it into CPS-ed code. That's a classic FP transformation (indeed it's one of the paradigmatic examples of first-class functions/higher-order functions) and one of the most popular methods of desugaring imperative-looking code in a functional context.
For syntax, the idea of hiding all that CPS behind an imperative-looking syntax sugar is the whole reason why do-notation and its descendants exist. (Indeed, there's an even deeper connection there specifically around CPS and monads: https://www.schoolofhaskell.com/school/to-infinity-and-beyon...)
But my point was simply that there's a pretty straight line from CPS, monads, and do-notation to async-await and so I think it's pretty fair to say that async-await is rooted in the FP tradition.
Futures/promises (they mean different things in different languages), like many other things, form monads. In fact async-await is a specialization of various monad syntactic sugars that try to eliminate long callback chains that commonly affect many different sorts of monads.
Hence things like Haskell's do-notation are direct precursors to async-await (some libraries such as Scala's monadless https://github.com/monadless/monadless make it even more explicit, there lift and unlift are exactly generalized versions of async and await).
To see how async-await might be generalized, one could turn to various other specializations of the same syntax, e.g. an async that denotes a random variable and an await that draws once from the random variable.
To see the correspondence with a flatMap method (which is the main component of a monad), it's enough to look at the equivalent callback-heavy code and see that it looks something like