dotnet-asynclisted
Install: claude install-skill AllThingsSmitty/agent-skills
# C# Async Patterns
C#'s async/await model is powerful but has a handful of well-known pitfalls — most production async bugs fall into three categories: deadlocks from blocking on async code, unobserved exceptions from `async void`, and missing cancellation support. Know these cold.
## Never block on async code
Calling `.Result` or `.Wait()` on a Task blocks the current thread and can deadlock in contexts with a synchronization context (WinForms, WPF, old ASP.NET):
```csharp
// Bad: can deadlock in sync-context environments
var user = GetUserAsync(id).Result;
var data = FetchDataAsync().GetAwaiter().GetResult();
// Good: async all the way up
var user = await GetUserAsync(id);
```
In ASP.NET Core there is no synchronization context, so `.GetAwaiter().GetResult()` won't deadlock there — but it still blocks a thread pool thread, defeating the purpose of async. Make it async all the way.
If you genuinely need to call async from sync (e.g., a console app entry point or legacy code), use `Task.Run(() => method()).GetAwaiter().GetResult()` as a last resort — but don't normalize it.
## `async void` is dangerous
`async void` methods can't be awaited. If they throw, the exception is unobservable and crashes the process (or is silently swallowed depending on the host). Use `async Task` everywhere except event handlers.
```csharp
// Bad: exception from SendEmailAsync is unobservable
async void NotifyUser(User user)
{
await SendEmailAsync(user.Email);
}
// Good: exception c