It doesn't have to be viral. C#'s tasks have a blocking Wait[0] method which allows you to use an asynchronous Task without changing the signature of your synchronous function. The tradeoff is more verbosity.
As noted in another comment, Wait is extremely prone to deadlocks - if you happen to Wait on a thread that's running the event loop, then no task that's scheduled on that loop can execute until Wait returns. So if you're waiting on such a task, or on a task that depends (no matter how indirectly) on such a task, you get a deadlock.
Now, if you're writing a library, you pretty much cannot assume anything about the event loop and what's scheduled on it. If your library invokes a callback at any point, all bets are off, because you don't know which tasks that callback may have scheduled, or which tasks it's waiting on. Similarly, if you provide a callback to a library, you also don't know which tasks you might block by waiting.
So, in effect, the only safe place to wait is on a background thread that was specifically spawned for that purpose, and that is guaranteed to have no event loop running on it.
[0] - https://msdn.microsoft.com/en-us/library/dd235635(v=vs.110)....