Introduction to async and await Keywords
async and await are two C# keywords that let you write asynchronous code that looks like normal synchronous code. They were added in C# 5.0 (with .NET Framework 4.5, in 2012), and today they are used in almost every .NET application: ASP.NET Core, Entity Framework Core, Blazor, MAUI, WPF and more.
asyncis written on a method. It says: "this method contains asynchronous waiting".awaitis written before a task. It says: "wait here for this task to finish, but don't block the thread while waiting".
Why were async and await added?
Asynchronous code existed in .NET before C# 5, but it was hard to write. You had to pass callbacks ("call this method when you are done"), and the code got split into many pieces. Look at the same logic written with ContinueWith (the old way) and with await:
// Old way: callbacks with ContinueWith
static Task ShowUserOldWay()
{
return GetUserNameAsync(1).ContinueWith(task =>
{
string name = task.Result;
return GetOrderCountAsync(name).ContinueWith(task2 =>
{
Console.WriteLine($"{name} has {task2.Result} orders");
});
}).Unwrap();
}
// New way: async and await
static async Task ShowUser()
{
string name = await GetUserNameAsync(1);
int orders = await GetOrderCountAsync(name);
Console.WriteLine($"{name} has {orders} orders");
}
The second version reads from top to bottom, exactly like normal code, but it is still fully asynchronous. Try/catch, loops and if statements also work normally with await. That is the whole idea of these keywords.
Your first async and await program
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Console.WriteLine("Fetching the user...");
string user = await GetUserNameAsync(1);
Console.WriteLine($"Hello, {user}!");
}
static async Task<string> GetUserNameAsync(int id)
{
await Task.Delay(1000); // pretend this is a database call
return "Rahul";
}
}
Let's read it line by line:
static async Task Main():Mainis markedasyncso we can useawaitinside it. It returnsTask(asyncMainis allowed from C# 7.1).async Task<string> GetUserNameAsync(int id): an async method that will give back astringin the future.await Task.Delay(1000): waits 1 second without blocking any thread.return "Rahul";: we return a plainstring. The compiler wraps it in theTask<string>for us.string user = await GetUserNameAsync(1);:awaittakes theTask<string>and gives us thestringinside it when it is ready.
Return types of an async method
| Return type | Use it when | Example |
|---|---|---|
Task | The method returns nothing (like void) | async Task SaveOrderAsync(Order order) |
Task<T> | The method returns a value of type T | async Task<int> GetCountAsync() |
void | Only for event handlers (button clicks) | async void btnSave_Click(object sender, EventArgs e) |
ValueTask / ValueTask<T> | Advanced: hot paths that usually finish without waiting (e.g. a cache hit) | async ValueTask<Product> GetFromCacheAsync(int id) |
IAsyncEnumerable<T> | Returning many items one by one asynchronously (C# 8+) | async IAsyncEnumerable<Order> StreamOrdersAsync() |
Basic rules to remember
awaitcan only be used inside a method markedasync.- By convention, async method names end with Async:
GetUserNameAsync,SaveChangesAsync. - Prefer returning
Taskinstead ofvoid, so the caller can await it and catch its errors. - Once you start using async, use it "all the way up": if a method awaits something, its caller should usually await it too.
- Don't block on async code with
.Resultor.Wait(); useawait.
A real ASP.NET Core example
This is how async and await look in a real Web API controller using Entity Framework Core:
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly AppDbContext _context;
public ProductsController(AppDbContext context)
{
_context = context;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetProduct(int id)
{
Product? product = await _context.Products.FindAsync(id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
}
While SQL Server looks for the product, the request thread is returned to the thread pool and can handle other users' requests.
Summary
asyncmarks a method that uses asynchronous waiting;awaitwaits for a task without blocking.- They were added in C# 5.0 to replace messy callback code.
- Async code written with them reads top to bottom like normal code.
- Async methods return
Task,Task<T>,ValueTaskor (only for events)void. - Name async methods with the Async suffix and use async all the way.
In the next two lessons we look at what async really does and what await really does.