C# probably has some of the best async primitives of any language. I still managed to be bitten by the very common issue of error logging getting lost in a hidden callback that doesn't look like a callback because of the magic of the async keyword (a common issue in node as well).
I'm convinced that the async style of web servers is largely a step in the wrong direction. A leaky abstraction that we don't need.
A better model already exists - thread per connection - for the majority of what we want as web developers. There are some good things about allowing us to manage our own waits but those things are rare.
It reminds me of the choice of when to manage your own memory - usually we don't need to, but sometimes we do. Most languages programmers use today decided, rightly, that memory management was an exception and not the norm. We are more productive today for the reduction in cognitive load for most applications as we decide not to manage our own memory.
Thread per connection has gotten a bad name for performance which is pretty false. You can create the exact same underlying abstraction with green threads for more concurrency when you absolutely need it (but mostly you don't). The code we get from not using async is reduced in complexity substantially.
I'm disappointed that django is on the async bandwagon. It means that I may have to make another framework choice in the future.