It would be awesome if async/await reduced code complexity instead of just hiding it. But when complexity is unavoidable, we have to make it manageable.
Dynamic languages have good generic forms of memoization, but I haven't found one I'm happy with in C#. This is a sketch of a generic memoization in C#.