Codectionary / Developer documentation / C#

async and await

The async and await keywords let you write asynchronous code that reads almost like ordinary sequential code. Marking a method async allows it to use await, which pauses execution at that point until the awaited operation completes, without blocking the calling thread - freeing it up to do other work in the meantime. This is essential for keeping applications responsive during I/O-bound work like network calls, file access, or database queries.

Syntax

async Task MethodName() {\n  await SomeAsyncOperation();\n}

Examples

A Basic Async Method

Marking a method async and awaiting a delay.

async Task GreetAfterDelayAsync()
{
    Console.WriteLine("Starting...");
    await Task.Delay(1000);  // pauses here without blocking the thread
    Console.WriteLine("One second later!");
}

await GreetAfterDelayAsync();

Async Methods That Return a Value

async Task<T> is the async equivalent of a method that returns T.

async Task<int> GetNumberAsync()
{
    await Task.Delay(500);
    return 42;
}

int result = await GetNumberAsync();
Console.WriteLine(result);  // 42

Why Async Matters: Not Blocking the Thread

Comparing a blocking synchronous wait to a non-blocking async one.

// Synchronous: blocks the calling thread entirely for 2 seconds
void BlockingWait()
{
    Thread.Sleep(2000);
    Console.WriteLine("Done (blocked the thread the whole time)");
}

// Asynchronous: frees the thread to do other work while waiting
async Task NonBlockingWaitAsync()
{
    await Task.Delay(2000);
    Console.WriteLine("Done (thread was free to do other work)");
}

await NonBlockingWaitAsync();

Awaiting Multiple Steps in Sequence

await pauses at each step, but the code still reads top-to-bottom like synchronous code.

async Task<string> FetchDataAsync()
{
    Console.WriteLine("Step 1: connecting...");
    await Task.Delay(300);

    Console.WriteLine("Step 2: downloading...");
    await Task.Delay(300);

    return "data loaded";
}

string data = await FetchDataAsync();
Console.WriteLine(data);

Best practices

  • Name async methods with an "Async" suffix (GetDataAsync, not GetData) - a strong, widely followed C# convention
  • Use async Task instead of async void for any method except top-level event handlers - async void methods cannot be awaited and make error handling much harder
  • Await asynchronous operations all the way up the call chain rather than blocking on them with .Result or .Wait(), which can cause deadlocks in some contexts (like UI apps)
  • Reserve async/await for genuinely I/O-bound work (network, disk, database) - it does not speed up CPU-bound computation on its own

At a glance

Purpose
Applications on the .NET platform
File extension
.cs
Runs in
.NET runtime
Usually used with
.NET SDK and libraries

Specifications & further reading

Related C# documentation

Task & Task<T>
Task represents an asynchronous operation that may still be running, and Task<T> represents one that will eventually produce a value of type T. Tasks are the building block that async/await is built on top of - an async method actually returns a Task (or Task<T>) that the caller can await, check the status of, or attach continuations to.
Task.WhenAll & Task.WhenAny
Task.WhenAll() lets you run multiple independent asynchronous operations concurrently and await all of them together, completing once every task finishes - much faster than awaiting them one at a time. Task.WhenAny() completes as soon as the first of several tasks finishes, useful for timeout patterns or taking whichever result arrives first.
CancellationToken
CancellationToken provides a standard, cooperative way to signal that an asynchronous operation should stop before it completes naturally. A CancellationTokenSource creates the token and controls when cancellation is requested; the token itself is passed into async methods, which check it periodically (or pass it to another cancellable operation) and can throw an OperationCanceledException to unwind cleanly.
Async Pitfalls: void vs Task, ConfigureAwait
A few recurring mistakes trip up many C# developers new to async: using async void instead of async Task (which makes exceptions impossible to catch normally), and blocking on async code with .Result or .Wait() (which can cause a deadlock in contexts with a synchronization context, like older ASP.NET or WPF apps). Understanding these pitfalls up front avoids some of the most common async-related bugs.