3 ms·
What you are describing is really “async-never.” And in fact, the programming model that async/await gives you is in fact, blocking. To the programmer, awaiting
by jchw 5y ago
What you are describing is really “async-never.” And in fact, the programming model that async/await gives you is in fact, blocking. To the programmer, awaiting is no different from blocking a function.
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.”
- slver 5y ago> What you are describing is really “async-never.” And in fact, the programming model that async/await gives you is in fact, blocking. To the programmer, awaiting is no different from blocking a function. Not exactly, I rather proposed async calls are explicit, instead of blocking calls being explicit. Most people have no problem with the APPEARANCE of blocking. They mind if it's actually blocking under the hood. It's normal that when you issue some HTTP request, for ex. you can't proceed processing the response until you get it. One way or another "you await" that moment. Regarding whether it's free or not, I still don't quite understand the problem TBH. An event loop isn't a hard addition to any language. The only thing I wonder about is stack fragmentation.