I'd beg to differ on the easy to understand... last time i worked with this stuff like ConfigureAwait only has cargo cults and no real good explanation, and wasn't covered in any recent version of eg. CLR via C#... Distributed tracing and thread management were totally bitten by async, also the way iterators and LINQ were broken... that was when I got off the train, as it broke the best abstractions for line of business code so that instead of designing an IO pipeline lazy developers could simple put Task.Run around blocking IO calls at the bottom of the codebas, and ... yeah, we are back at function coloring.
This brings me to: I winder for what kind of apps did you find it a delight, because for extensive data wrangling leveraging multiple calls it was a pain.
I'm using C# for the backend of a web application, not legacy but .NET Core from the start.
Something which was as easy as an IO call in the middle of a function running on a thread pool becomes a PITA if the IO call is cleverly async-ized (by wrapping the original sync IO method into a Task.Run(), but actually this is the better case!) ad then removed from the public API... (cannot recall the exact library, but it was a pain, as it was some core stuff, maybe vendored by MS themselves)
Also back then it caused us loosing correlation IDs as those were thread locals in traditional pre-async ASP.Net Core (think version 0.9-2.0 when I worked on that part, then up until core 4 and the async mania continuos broke readable calculation code in LINQ, TPL based performant data pipelines, etc...) and with async execution started to migrate between threads... If you needed high performance you already used threads, because it was not a classic web application, but a specialized optimization algorithm with a web api... and if you need more than CRUD, and use all the cores (and SIMD) to squeeze out all performance you have, you hit these problems right away. These all could be solved, but it actually broke stuff, a lot of stuff, as developers jumped on the fad, and it their sync code was turned into a .ConfigureAwait(false /* Microsoft blogs talk a lot about it without a clear explanation, so every cool kid does this */).GetAwaiter().GetResult()... then you some of your threaded code silently broken if you dare used Thread Locals... Faded memories, but that was when they f'ed up the C# language.
...oooh, that very much sounds like System.Data.SqlClient :)
Indeed, you need a crapload of ConfigureAwait() in desktop UI code, and it infects all underlying libraries that might have to run from a desktop app. The worst thing they ever added to our beautiful language.
My experience with Go is a while back, before it got generics. While I liked the overall idea of a language with little syntax and a focus on simplicity, I didn't really like Go in the end. It was a weird mix of too low-level and high level code. It exposed low level details that I don't need to think about in C#, and it is missing convenient abstractions like LINQ.
Personally, I like async/await in C# more than Go concurrency. I know this is heretical here, but I don't care about function coloring or any of the abstract advantages of other concurrency approaches. Async/Await in C# works for me (in non-legacy code) and is easy to understand.