Callbacks are ugly when you have to deal with them directly. But a little bit of syntactic sugar to capture the current continuation as a callback - such as C# await, or the now-repurposed yield in Python - makes it very easy and straightforward to write callback-based async code.
Apparently, I wasn't paying as much attention to the state of async in Python 3.5 as I should have. Looks like they actually have the most featureful implementation of that right now, complete with async for all constructs where it can be sensibly implemented and provide some benefits (like for-loops and with-blocks).