It prints "hello world", unsurprisingly, but at each await point, e.g. hello = await async_hello(), does the coroutine created by calling async def main() returns (or yields) back the control to the runtime scheduler provided by asyncio?
Context
In Rust, it appears that this is the case, that each await points yields back to the caller, or the runtime, e.g. tokio runtime, an anonymous Future type that implements the Future trait on which .poll(...) can be called on.
This question stems from an overly repeated dogma that the Python thread 'blocks' at each await point in Python, which is of course, markedly different in Rust, i.e. in Rust, each await point does not block.
I am asking this question in the Rust forum because it is far likely that Rustaceans to know about async Python than async Pythonista to know about Rust.
It must (given that asyncio is single-threaded by default and executes the subroutines on the same thread as the event loop) or otherwise, who would drive the coroutine to completion? The awaitable must yield back to the executor for it to do the necessary work, like checking OS file descriptors and updating timers (running the event loop). From the docs I linked above:
An event loop runs in a thread (typically the main thread) and executes all callbacks and Tasks in its thread. While a Task is running in the event loop, no other Tasks can run in the same thread. When a Task executes an await expression, the running Task gets suspended, and the event loop executes the next Task.
Nitpick - await yields back to executor if there is actually something to await for, that is, futures::ready().await does not yield, it continues execution immediately.
JavaScript's async is very different because it is not based on coroutines at all. A JavaScript promise is not any kind of coroutine; instead, it is a handle to a result value that will be provided later, like the receiving side of Rust's async oneshot channels except shareable. Scheduling the code that resolves the promise is handled completely separately; the basic operation is "run this closure when you see this promise resolve" (then()) and async functions are effectively built out of that.