When Does Calling .Result Deadlock, and When Does It Not?
.Result and .Wait() block the current thread. When await suspends, it captures the current SynchronizationContext so that the code after it runs back on that same context. If the context only allows one thread at a time — a WPF UI thread, an ASP.NET Framework request context — and that thread is the one .Result is blocking, the continuation can never get in: deadlock. Console apps and ASP.NET Core have no SynchronizationContext, so the continuation runs on the thread pool and nothing gets stuck. The real fix is async all the way.
The code below runs perfectly in a console app, and freezes solid the moment you paste it verbatim into an ASP.NET Framework controller or a button handler in WPF:
public string GetCustomerName(int id)
{
return GetCustomerAsync(id).Result; // hangs here
}
private async Task<string> GetCustomerAsync(int id)
{
var response = await _http.GetStringAsync($"/customers/{id}");
return Parse(response).Name;
}
No exception, no stack trace, no timeout. The thread simply stops forever. This is not HttpClient's fault, and async is not "broken" — it is the direct consequence of a design decision in how await works.
