async and await
Keep apps responsive while waiting for files, networks and databases: Task, async methods, await, running waits together, cancellation and the classic mistakes.
async and await let a method pause while it waits without blocking the thread, and continue when the result arrives.
Task and Task<T>
Task represents work that will finish in the future. Task<T> will also produce a value of type T. Asynchronous .NET methods return tasks and are named with the suffix Async: File.ReadAllTextAsync, HttpClient.GetStringAsync.
Writing an async method
public async Task<int> CountMembersAsync(string path){ string json = await File.ReadAllTextAsync(path); // thread is free while the disk works var model = JsonSerializer.Deserialize<Model>(json)!; return model.Members.Count; // wrapped in Task<int> automatically} // caller (in a WPF button handler)private async void LoadButton_Click(object sender, RoutedEventArgs e){ StatusText.Text = "Loading…"; int n = await CountMembersAsync("tower.json"); StatusText.Text = $"{n} members"; // back on the UI thread}Mark the method async; returnTask,Task<T>(orValueTask).awaitpauses this method until the task completes; the thread goes off to do other work (like repainting the UI).After the await, code continues, on the UI thread in desktop apps, so you can update controls. async voidis only for event handlers. Everywhere else returnTask, so callers can await and catch errors.
Waiting for several things together
var http = new HttpClient(); Task<string> a = http.GetStringAsync("https://example.com/a");Task<string> b = http.GetStringAsync("https://example.com/b"); // both requests in flight string[] results = await Task.WhenAll(a, b); // total ≈ the slower oneConsole.WriteLine(results[0].Length + results[1].Length); Task<string> first = await Task.WhenAny(a, b); // whichever finishes firstCancellation
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); // auto-cancel after 10 sCancelButton.Click += (_, _) => cts.Cancel(); try{ await ExportAsync("tower.ifc", cts.Token);}catch (OperationCanceledException){ StatusText.Text = "Export cancelled";} async Task ExportAsync(string path, CancellationToken token){ foreach (var member in members) { token.ThrowIfCancellationRequested(); // check regularly await WriteMemberAsync(member, token); // pass the token down }}Classic mistakes
| Mistake | Why it hurts | Instead |
|---|---|---|
task.Result or task.Wait() | await task | |
async void methods | Task | |
await | ||
await in a loop for independent work | Task.WhenAll | |
Task.Run around I/O methods | …Async method |
static async Task<long> TotalSizeAsync(string[] paths) that reads all files concurrently with File.ReadAllBytesAsync and returns the total number of bytes. Print the result for two files.
Show the solution
static async Task<long> TotalSizeAsync(string[] paths){ Task<byte[]>[] reads = paths.Select(p => File.ReadAllBytesAsync(p)).ToArray(); byte[][] contents = await Task.WhenAll(reads); return contents.Sum(c => (long)c.Length);} long total = await TotalSizeAsync(new[] { "a.ifc", "b.ifc" });Console.WriteLine($"{total:N0} bytes");4,812,330 bytes
Remember
asyncmethods returnTask/Task<T>and useawaitto wait without blocking.Await I/O to keep UIs responsive and servers scalable. Start independent work first, then await Task.WhenAll(...).Avoid .Result,.Wait()andasync void(except event handlers); support cancellation.