Asynchronous Everything (2015)
joeduffyblog.com
Asynchronous Everything (2015)
1–10 of 26 posts
Re: Asynchronous Everything (2015)
#2Not only it makes it more consistent, but you can implement running a synchronous function into a separate thread just by doing "async foo()". Can't get easier.
Lua already has the right idea here (check coroutines), and why it's so useful in game scripts.
Re: Asynchronous Everything (2015)
#3Asynchronous Everything - https://news.ycombinator.com/item?id=10600573 - Nov 2015 (8 comments)
Re: Asynchronous Everything (2015)
#4Async will continue to look awkward until we drop explicit "await" on function calls. Awaiting should be the default. If I call "async foo()" then I get back a promise I can explicitly await, but there should be no semantic difference between calling a synchronous function or asynchronous without modifiers. Not only it makes it more consistent, but you can implement running a synchronous function into a separate thre…
All functions are async, calling functions does an implicit await, and the go keyword let's you spawn a task without await. (At least that's what semantically happens) This way you can write seemingly imperative blocking code, and have it work fully asynchronously.
It's great, freeing, and avoids the function coloring problem. It's really the feature I miss most when writing other languages.
Though I do understand why performance oriented languages like Rust might want to approach it differently.
I don't, however, see a reason for non-performance oriented ones to use function coloring (unless they're doing full semicolon customization, like Haskell with its monad stacks).
Re: Asynchronous Everything (2015)
#5Async will continue to look awkward until we drop explicit "await" on function calls. Awaiting should be the default. If I call "async foo()" then I get back a promise I can explicitly await, but there should be no semantic difference between calling a synchronous function or asynchronous without modifiers. Not only it makes it more consistent, but you can implement running a synchronous function into a separate thre…
Agreed. That's basically what Go does. All functions are async, calling functions does an implicit await, and the go keyword let's you spawn a task without await. (At least that's what semantically happens) This way you can write seemingly imperative blocking code, and have it work fully asynchronously. It's great, freeing, and avoids the function coloring problem. It's really the feature I miss most when writing oth…
Re: Asynchronous Everything (2015)
#6Async will continue to look awkward until we drop explicit "await" on function calls. Awaiting should be the default. If I call "async foo()" then I get back a promise I can explicitly await, but there should be no semantic difference between calling a synchronous function or asynchronous without modifiers. Not only it makes it more consistent, but you can implement running a synchronous function into a separate thre…
Agreed. That's basically what Go does. All functions are async, calling functions does an implicit await, and the go keyword let's you spawn a task without await. (At least that's what semantically happens) This way you can write seemingly imperative blocking code, and have it work fully asynchronously. It's great, freeing, and avoids the function coloring problem. It's really the feature I miss most when writing oth…
The thing Rust gets wrong however, is that there should be a smooth path from ergonomics to performance.
Re: Asynchronous Everything (2015)
#7Async will continue to look awkward until we drop explicit "await" on function calls. Awaiting should be the default. If I call "async foo()" then I get back a promise I can explicitly await, but there should be no semantic difference between calling a synchronous function or asynchronous without modifiers. Not only it makes it more consistent, but you can implement running a synchronous function into a separate thre…
Re: Asynchronous Everything (2015)
#8Async will continue to look awkward until we drop explicit "await" on function calls. Awaiting should be the default. If I call "async foo()" then I get back a promise I can explicitly await, but there should be no semantic difference between calling a synchronous function or asynchronous without modifiers. Not only it makes it more consistent, but you can implement running a synchronous function into a separate thre…
In practice though, async/await is a specific way to implement a blocking programming model with non-blocking internals. It basically breaks functions down into state machines so they can be executed incrementally. The other way is basically lightweight threads with semi-cooperative scheduling, like Go does, and what you could do in Lua (though Lua cothreads are technically quite different and their own bag of worms.)
This model works nicely in Go, whose lightweight threads pay the cost of C interop in some nanoseconds of overhead, and whose compiler adds yields in key points to aid platforms that can’t (yet) support some form of usermode preemption.
However, in JS, we already have the event loop. Control always returns back to the event loop. This has worked this way for decades in most places where JS is used, so it’s unlikely to change soon. Because of this, functions in JS can’t block. async/await is ideal here because the decomposed functions can return to the event loop when they await, for a callback to trigger the next fragment.
And in Rust, the language has use cases and environments that it runs in where the runtime costs and requirements of a lightweight thread scheduler are not acceptable. So yet again, async/await is ideal, because it allows for a blocking programming model that doesn’t compromise on the other harder requirements.
So then, should all functions just be “async”, and all function calls be “awaited?” I think this will prove complex and still undesirable. For one thing, async is hard in statically compiled languages. Your state will generally be the sum of the state of functions you await, which proves very complex for say, recursive async functions. Even when not, async functions require some kind of scheduler that manages all of the running routines. In a low level language, you may not want to impose an async routine scheduler or even async/await in the first place onto applications.
It’s unfortunate, but I think we’re finding at least local maxima to the solutions to async programming, and running into limits that appear harder to conquer. A blocking programming model is convenient, but if you want a blocking programming model with non-blocking behavior, it can’t come entirely “free.”
Re: Asynchronous Everything (2015)
#9Async will continue to look awkward until we drop explicit "await" on function calls. Awaiting should be the default. If I call "async foo()" then I get back a promise I can explicitly await, but there should be no semantic difference between calling a synchronous function or asynchronous without modifiers. Not only it makes it more consistent, but you can implement running a synchronous function into a separate thre…