Intermediate · Exceptions, delegates, events and async · topic 29 of 43 · 35 min
async and await: waiting without blocking
Explanation
The problem: waiting wastes threads
A thread is a worker that runs your code. Calling a payment gateway might take 800 milliseconds, and almost all of it is just waiting for the reply. If the thread sits blocked during that wait, it cannot do anything else. In a desktop app the window freezes. In a web server, each blocked thread is one fewer request it can serve, so under load the site slows down or fails.
Task and Task<T>
A Task is a promise of work that will complete in the future. Task<T> also promises a result of type T. You do not usually create them by hand; .NET methods that do I/O return them, and their names end in Async: File.ReadAllTextAsync, HttpClient.GetStringAsync, SaveChangesAsync.
Writing an async method
static async Task<string> GetGreetingAsync(string name)
{
await Task.Delay(1000); // pretend this is a slow network call
return $"Namaste, {name}";
}
string message = await GetGreetingAsync("Asha"); // top-level await works in Program.cs
Console.WriteLine(message);What await really does
- The method runs normally until it reaches an await on something that is not finished yet.
- It pauses there and returns an unfinished Task to its caller. The thread is free to do other work.
- When the awaited operation finishes, the rest of the method continues (possibly on another thread).
- When the method returns, its Task completes, and whoever awaited it continues.
Console.WriteLine("1");
Task work = SlowAsync();
Console.WriteLine("2");
await work;
Console.WriteLine("4");
static async Task SlowAsync()
{
await Task.Delay(500);
Console.WriteLine("3");
}Doing several things at once: Task.WhenAll
using System.Diagnostics;
var watch = Stopwatch.StartNew();
Task<decimal> fare = GetFareAsync(); // starts now
Task<int> seats = GetSeatsAsync(); // also starts now
await Task.WhenAll(fare, seats); // wait for both
Console.WriteLine($"Fare {fare.Result}, seats {seats.Result}, took about {watch.ElapsedMilliseconds} ms");
static async Task<decimal> GetFareAsync() { await Task.Delay(2000); return 1450m; }
static async Task<int> GetSeatsAsync() { await Task.Delay(1500); return 23; }A real example: calling a web API
using var http = new HttpClient();
http.Timeout = TimeSpan.FromSeconds(10);
string body = await http.GetStringAsync("https://api.github.com/zen",
CancellationToken.None);The rules that prevent most async bugs
| Do | Avoid | Why |
|---|---|---|
| async Task / async Task<T> | async void | Exceptions from async void cannot be caught by the caller and can crash the app. Only UI event handlers should be async void. |
| await task | task.Result or task.Wait() | Blocking on async code wastes a thread and can deadlock in UI apps and older ASP.NET. |
| await every Task you start | Calling an async method without await | The work runs unobserved; errors vanish. The compiler warns (CS4014). |
| Async all the way up | Mixing sync and async halfway | Once a method awaits, its callers should be async too. |
| Name methods ...Async | Unclear names | Callers know they must await. |
I/O-bound versus CPU-bound
async helps with I/O-bound work: waiting for network, disk or database. It does not make calculations faster. For heavy CPU work, like resizing 500 photos, the work itself needs a thread; in a desktop app you can move it off the UI thread with await Task.Run(() => Resize(photos)). In ASP.NET Core, avoid wrapping CPU work in Task.Run just to look async.
Exceptions and cancellation
If an awaited task fails, await rethrows its exception, so a normal try/catch around await works. Long operations usually accept a CancellationToken so they can be stopped, for example when a user leaves the page.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
async and await: waiting without blocking · 1/7
In this lesson your code will wait for slow things without blocking.
Prefer a video by a teacher? Find free videos on “C# async await Task explained for beginners Task.WhenAll” on YouTube ↗
In real life: A travel app loading a trip page
When you open a trip on a travel app, it fetches the train status, the weather at the destination and the hotel booking. Done one after another, each wait adds up. Started together with Task.WhenAll, the page loads in roughly the time of the slowest call.
var status = GetTrainStatusAsync("12951");
var weather = GetWeatherAsync("Mumbai");
var hotel = GetHotelAsync(bookingId: 88231);
await Task.WhenAll(status, weather, hotel);
Console.WriteLine($"{await status} | {await weather} | {await hotel}");
static async Task<string> GetTrainStatusAsync(string trainNo) { await Task.Delay(900); return $"{trainNo}: on time"; }
static async Task<string> GetWeatherAsync(string city) { await Task.Delay(600); return $"{city}: 31 C"; }
static async Task<string> GetHotelAsync(int bookingId) { await Task.Delay(700); return $"Booking {bookingId}: confirmed"; }Practice (0/4 done)
What should an async method that returns no value usually return?
In what order are the letters printed? Console.Write("A"); var t = WorkAsync(); Console.Write("B"); await t; Console.Write("D"); static async Task WorkAsync() { await Task.Delay(100); Console.Write("C"); }
Two independent calls take 2 s and 3 s. Started together and awaited with Task.WhenAll, about how long does it take?
Write an async method GetPriceAsync(string item) that waits a different time per item with Task.Delay and returns a price. Fetch prices for "rice", "dal" and "oil" first one by one, then all together with Task.WhenAll, and print how long each approach took using Stopwatch.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Files and JSON with System.Text.JsonExceptions, delegates, events and async · same module
- Delegates, lambdas, Func, Action and PredicateExceptions, delegates, events and async · same module
- Async Rust with tokioRust Programming · Concurrency and async
- Futures and async/awaitFlutter and Dart · Async Dart and packages
- async and awaitJavaScript Programming (language in depth) · Asynchronous JavaScript
- async/await and error handlingFrontend Web Development · Async JavaScript, modules and tools
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What kind of work benefits most from async/await?
Answer: Waiting for network, disk or database (I/O)
async frees threads during I/O waits.
Why avoid task.Result on an unfinished task?
Answer: It blocks the thread and can deadlock in some apps
Blocking defeats the purpose of async and can deadlock.
When is async void acceptable?
Answer: For UI event handlers
Event handlers must return void; everywhere else return Task.
What does await do if the awaited task throws?
Answer: Rethrows the exception so try/catch can handle it
Exceptions flow through await normally.
What is the convention for naming methods that return a Task?
Answer: End with Async
GetDataAsync tells the caller to await it.
What is the danger of a long CPU loop inside an async task?Related
Answer: It blocks the worker thread, so other tasks stall
Tasks must yield at .await points; use spawn_blocking for heavy work.
Does await block the whole Dart program?Related
Answer: No, it pauses only the current function while the event loop keeps working
Other events continue.
Two independent requests each take 1 s. With await one after the other, the total is about…Related
Three independent requests each take about 1 second. How long does await Promise.all([a(), b(), c()]) take?Related
Answer: About 1 second
Promise.all starts them together, so the total is about the slowest one. Awaiting them one by one would take about 3 seconds.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 1 more topic in “Exceptions, delegates, events and async” before its exam: Files and JSON with System.Text.Json.
- Module 7 exam: Exceptions, delegates, events and async · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Web APIs with ASP.NET Core and EF Core. 43 topics left in the course before the final exam.