Skip to content

Comment on A Design Space Exploration of Async/Awaitparent

Comments

The principal difference between dispatching in a thread-based framework and async/await is that async/await allows you to program sequences of asynchronous operations much more easily. No more separation of code that initiates an async operation and the code that handles the result!

These higher level primitives that they mention provide the primitives for exactly that. This can be found in other paradigms besides async/await.

I do find that the ergonomics of this are highly dependent on a few features of a language runtime, without which it all falls apart. Or you need language specific syntax and typically a single standard implementation.

Of course. I don't think a language can support async/await without a library implementation, or the language features that support it.

And I can't honestly think of another paradigm that doesn't require callback functions or lambdas that, ergonomically, end up producing function implementations that end up drifting off the right side of the screen for anything more than a couple of sequential asynchronous operations.

I didn’t say async/await need language features (though they usually do), I meant that high level async orchestration can be represented with suitable language features - often in ways that are more interesting than async/await.

“I can't honestly think of another paradigm that doesn't require callback functions” … Haskell, Scala, Rust all use various approaches to asynchrony that leverage these language features to provide you very usable abstractions without the “callbacks” you mention. Some of those lean on lambdas, but the scrolling off the right issue hasn’t been an issue there for at least a decade now.

Scala’s direct mode is interesting, as an example of library driven, blocking/imperative style interactions that provide most of the benefits of the monadic effect style, but in a way that’s far easier for a human to write and review.

This might not be ready for mass adoption yet, but I think it’s a sneak peek of where we’ll see some languages move to.

The principal difference between threads and async/await has to be specified in at least 9 dimensions...

Very similar dimensions also apply to threads, the async/await is not really that different there.

No, they don't. With threads almost all of those properties are explicit choices that the developer has to implement.

At most you get hidden behavior on exception propagation and end-of-life extent.

You get a few dimensions. Some of the dimensions listed in the article for async/await become user library decisions rather than being baked into the threading. But you could still get things like, is each thread memory-isolated (Erlang, Pony?) or not, can you cancel them (imperative languages no, but Erlang and Haskell yes), is the concurrency structured or not, details around how and when the threads clean up (though perhaps arguably more related to memory management then thread management), can a thread be pre-emptively descheduled (though I'm not sure if there is any current system where the answer is "no", Go was "no" for a while).

If I sat down and made a careful study of all the threading implementations we might get up to a similar number of quirks.

I would suggest though that the dimensions are generally more likely to be corner cases. Some of the dimensions mentioned in that article are fairly in-your-face for an async/await implementation and can cause serious difficulties migrating between systems fairly quickly if you make the wrong assumptions, and writing correct async/await code that isn't just straightline "await everything immediately" code has to start taking some of those things into account very quickly. The equivalent for threading is more likely to only come up rarely and in more cases the correct answer is really "don't depend on that anyhow", e.g., rather than depending on exact details of how a thread is terminated to accomplish something, just cleanly send a message with your results to whoever it is waiting for it directly and let the runtime do the cleanup without your code witnessing any effects of it. Depending on these quirks in threading code is much more likely to be bad engineering practice, rather than necessary engineering practice in the async/await case.

Very much so and it can be argued that the difference between threads and ssync is just another dimension to compare on. For example, essentially everything that is involved in Structured Concurrency is as relevant to parallel scenarios as well.

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.