iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

async / await

C# async/await is built on the Task type. async functions can await tasks without blocking a thread. Properly used, you write linear code that scales like callbacks.

Tasks, cancellation, parallel, gotchas

EXAMPLE
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

// 1) The shape
async Task<string> FetchAsync(string url, CancellationToken ct)
{
    using var http = new HttpClient();
    var resp = await http.GetAsync(url, ct);   // doesn't block the thread
    resp.EnsureSuccessStatusCode();
    return await resp.Content.ReadAsStringAsync(ct);
}

// 2) Call it
string body = await FetchAsync("https://api.example.com/users", CancellationToken.None);

// 3) Parallel calls — Task.WhenAll for fire-them-all-at-once
string[] urls = { "a", "b", "c" };
string[] bodies = await Task.WhenAll(urls.Select(u => FetchAsync(u, CancellationToken.None)));

// 4) First-finished
Task<string> winner = await Task.WhenAny(
    FetchAsync("primary",   ct),
    FetchAsync("secondary", ct));
string data = await winner;

// 5) Cancellation
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
try
{
    var body = await FetchAsync(url, cts.Token);
}
catch (OperationCanceledException)
{
    // expected when cts expires or user cancels
}

// 6) ConfigureAwait — library code should add it
public async Task<string> LibAsync()
{
    var r = await SomethingAsync().ConfigureAwait(false);
    // After ConfigureAwait(false), don't touch UI state
    return r;
}

// 7) Async streams (C# 8+) — IAsyncEnumerable
async IAsyncEnumerable<string> ReadLinesAsync(string path, [EnumeratorCancellation] CancellationToken ct)
{
    using var sr = new System.IO.StreamReader(path);
    string? line;
    while ((line = await sr.ReadLineAsync(ct)) != null)
        yield return line;
}

await foreach (var line in ReadLinesAsync("big.log", ct))
{
    Console.WriteLine(line);
}

// 8) Common gotchas

// 8a) async void — DON'T (except event handlers)
// Exceptions escape to the SynchronizationContext and crash the process.
async void OnClick(...)         // OK for UI event handlers ONLY
{
    try { await DoAsync(); }
    catch (Exception e) { log.Error(e); }
}

// 8b) Sync-over-async — DEADLOCK risk
var bad = SomethingAsync().Result;          // BAD
var bad2 = SomethingAsync().GetAwaiter().GetResult();  // BAD on a SynchronizationContext
// Always await it, all the way up.

// 8c) Forgetting to await
FetchAsync(url);            // fire-and-forget — exceptions silently swallowed!
_ = FetchAsync(url);        // at least signals intent + still problematic

// 8d) Captured this in long-running tasks — beware lifetime

// 9) Background work — Task.Run for CPU-bound
int hash = await Task.Run(() => HeavyHash(data));
// Don't Task.Run for I/O — it just burns a thread

// 10) ValueTask — for hot paths where most awaits complete synchronously
async ValueTask<int> GetAsync(string key)
{
    if (cache.TryGetValue(key, out var v)) return v;     // no allocation
    return await db.GetAsync(key);                       // allocates Task only when needed
}

// 11) Channels — async producer/consumer pipelines
using var ch = System.Threading.Channels.Channel.CreateBounded<string>(100);
_ = Task.Run(async () => {
    await foreach (var msg in ch.Reader.ReadAllAsync()) Process(msg);
});
await ch.Writer.WriteAsync("hello");

// 12) Best practices
//   • Pass CancellationToken through your async API surface
//   • Use Task.WhenAll for independent parallelism
//   • ConfigureAwait(false) in libraries, default (true) in apps that update UI
//   • Avoid Thread.Sleep — use Task.Delay
//   • One try/catch around the awaited call, not three nested ones

Why it matters

Never .Result or .Wait() on a Task — that’s how UI apps deadlock and server apps starve the thread pool. Push the async all the way up the call stack.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
static async Task<string> FetchAsync(string url) {
    using var client = new HttpClient();
    return await client.GetStringAsync(url);
}
Try it Yourself »

Exercise

Await an async call.

string html = client.GetStringAsync(url);

Test yourself

Q1. An async method usually returns…
Q2. Inside an async method you await…
Q3. await on a successful Task<T> gives back…

Discussion

Loading…