That's because the best answer is "don't". You should really strive to be async all the way.
I can't use await because I have to be sure the method is finished before I do anything else.
Note that async
is serial (meaning the method won't continue past the await
until the inner method completes); it's just asynchronously serial instead of synchronously serial. So the same programming constructs and logic apply just the same:
public async Task MyPatientCallingMethodAsync(...)
{
// 1. call the method
var task = ThirdPartyAsync(...);
// 2. wait for ThirdPartyAsync to finish
await task;
// 3. return after
return;
}
This is exactly the approach you should take the vast, vast majority of the time.
However, if you positively, absolutely cannot do this, then your options are limited. Stephen Toub describes your options on his blog.
It is important to note that none of the options work in all scenarios and that every option has drawbacks and pitfalls. So none of us can say which one is best, or even which one would work since we don't know in what context your code is executing.
In summary, your options are:
- Synchronously block using
GetAwaiter().GetResult()
or Wait
(see @l3arnon's answer for the difference). E.g. ThirdPartyAsync(...).GetAwaiter().GetResult();
. The drawback to this approach is that deadlocks are a real possibility if called in a UI or ASP.NET context.
- Offload to a thread pool thread and block on that. E.g.,
Task.Run(() => ThirdPartyAsync(...)).GetAwaiter().GetResult();
. The drawback to this approach is that the third-party code must run on an alternate thread, so this won't work for code assuming a UI or ASP.NET context.
- Execute a nested message loop. E.g.,
AsyncContext.Run(() => ThirdPartyAsync(...));
. The drawbacks to this approach are that the nested loop may not do everything you need (i.e., it may not pump Win32 messages, which the third-party code may require), the nested context does not match expected contexts (i.e., this can cause ASP.NET calls to fail), and the nested loop may do too much (i.e., it may pump Win32 messages, which can cause unexpected reentrancy).
In short, although it sounds simple enough, "sync over async" is one of the most complicated things you can (try to) do. And this is the long explanation behind the common answer of "just use async all the way!" :)