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.

Read the syntax

An async method commonly returns Task or Task<T>; await suspends that method until an awaitable operation completes. It does not automatically create a new thread.

Task.Delay represents an asynchronous wait, unlike Thread.Sleep, which blocks the calling thread. Awaiting operations in sequence preserves their order; start independent tasks first when concurrency is needed.

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();
  1. Marking a method async and awaiting a delay.
  2. An async method commonly returns Task or Task<T>; await suspends that method until an awaitable operation completes. It does not automatically create a new thread.
  3. The method prints Starting..., awaits an approximately one-second delay, then prints One second later!. The caller awaits the method before continuing.

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
  1. async Task<T> is the async equivalent of a method that returns T.
  2. An async method commonly returns Task or Task<T>; await suspends that method until an awaitable operation completes. It does not automatically create a new thread.
  3. Task<int> carries a future int result. After the delay, return 42 completes the task; the caller’s await obtains and prints 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();
  1. Comparing a blocking synchronous wait to a non-blocking async one.
  2. An async method commonly returns Task or Task<T>; await suspends that method until an awaitable operation completes. It does not automatically create a new thread.
  3. The example calls only NonBlockingWaitAsync; BlockingWait is declared for comparison but never invoked. After a delay it prints the nonblocking completion message.

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);
  1. await pauses at each step, but the code still reads top-to-bottom like synchronous code.
  2. An async method commonly returns Task or Task<T>; await suspends that method until an awaitable operation completes. It does not automatically create a new thread.
  3. Each delay is awaited before the next step starts. The output shows connecting, downloading and data loaded in that order.

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

Runtime & versions

.NET runtime. Feature availability depends on the language and runtime version used by your project; consult the official reference below.

C# language reference

Specifications & further reading