I feel you, I really do, but it has it's place. Quite often you really do want to execute multiple different things in the background and wait for all of them to return before proceeding.
In pseudocode:
1. var logResults = Background writeLogServiceStarted() // Sent to different machine
2. var authoResults = Background performAuthorisation() // Perform by 3rd party
3. var userSettings = Background getUserSettings(request.currentUser) // Stored in DB
4. var results = Background executeQuery(request.query, authoResults) // Different DB
5. var response = Background generateResponse(results, userSettings)
6. wait (logResults, response)
7. transmitResponse (response)
The current async/await solution doesn't really make this as clear as the above though: The code is littered with some form of unwrapping/wrapping at every step hiding the actual intention, the call stack is marked as async making it hard to figure out where and when a sync function can make a call, etc.
The JVM guys are working on that with Loom, and, because of its multi-language nature, that can be brought across to many other languages too. Including, oddly, Rust, because Rust compiles with LLVM and GraalVM has a Truffle interpreter for Rust. I doubt anyone would actually want to run an app that way today especially as it's kind of cutting edge stuff and the Rust ecosystem is forcing async anyway, but in principle you could run a non-async Rust server on the JVM with millions of lightweight threads. It'd preserve the safety properties of the language and even the memory layouts, because Truffle doesn't force GC or Java-style memory layouts on the languages it runs. You can even AOT compile stuff but that requires the enterprise edition.
I have been coding since the late 70s -- the latter half of the last century. After all I have seen, async feels wrong.