The Kotlin people came up with a few useful notions around co-routines:
- functions that can be called asynchronous must be marked with suspend.
- functions marked as suspend can only be called from within a co-routine context. This is an abstraction that gives you a handle on resources consumed by your co-routines and some level of control over that.
- You can create/get a co-routine context in several ways and there's a global context (i.e. the main thread). Useful other contexts could be some web request or some thread pool. A context has dispatcher, a scope, and a few other things.
3) suspend functions calling other suspend functions implicitly await each other, i.e. preserve before/after semantics. It looks like normal code and there are no special keywords needed. IMHO this is genius compared to promise chaining and error handling you deal with in javascript which can become quite messy. Even with async await in js, you still need to return a Promise. In Kotlin all this is implied by using the suspend keyword on the function.
4) await is indeed something you do explicitly in a synchronous function only and it blocks the thread it is happening on. So, it's also something you should mostly avoid.
5) Co-routines can be terminated. This ensures that any still running async calls stop wasting cpu time. This is a problem in e.g. javascript where once you are awaiting something, you have no way to interrupt whatever it is you are awaiting.
6) pre-existing other asynchronous stuff in Java and its various frameworks can be adapted to co-routines quite easily.
This is a complicated topic and I'm sure there are a thing or two here that don't quite map to Rust that easily, which they've probably debated at length. I imagine tight memory and resource control is important for Rust. But it's a nice design for Kotlin at least. Compared to Rust, the development process was interesting as well. There was a long experimental feature cycle (1.1 and 1.2) during which you could opt into using it but during which there were also major changes based on the usage and feedback. I think they learned a lot during this phase. There are still new things coming that are still experimental (e.g. channels).