The Hidden Complexity of Async/Await
While async/await keywords appear consistent across programming languages, they actually implement vastly different underlying semantics. These differences stem from nine distinct design dimensions categorized by the task's start, end, and cancellation phases. Understanding these dimensions is essential because they cause the same code to produce different outputs across different runtimes.
key points
Language runtimes differ on 'Eagerness,' where some start executing an async function immediately while others create an inert object that only runs when awaited.
The 'Extent' of a task determines if it can outlive its spawning scope or if it must be cleaned up via cancellation or awaiting when the scope ends.
Cancellation behaviors vary widely, ranging from tasks that are completely unaware of cancellation to those that can respond to it in either a top-down or bottom-up manner.
community discussion
7 Positive[consensus]
Commenters generally appreciate the taxonomical approach to async/await design, agreeing that the complexity of asynchronous runtimes is often underestimated. There is a shared sentiment that while async/await simplifies sequential asynchronous logic, it introduces its own set of trade-offs compared to traditional threading or actor models. Many agree that a one-size-fits-all async model is impractical, especially for complex systems like multimedia players that require diverse concurrency primitives.
top insight
A significant perspective is that the perceived difficulty of threading stems from poor historical patterns, such as excessive shared state and semaphore use, rather than threading itself. By adopting structured concurrency, actor-like message passing, and worker pools, threading can be more manageable and sometimes less complex than async/await. This suggests that the 'difficulty' of concurrency is a design and pattern problem rather than a primitive problem.