Skip to content
async/await Basics
step 1/6

Reading — step 1 of 6

Learn

~3 min readAsync and Concurrency

async/await Basics

A thread that calls a blocking API — file read, HTTP request, database query — sits idle until the response arrives. async/await lets that thread go do other work in the meantime, while your code still reads top-to-bottom like ordinary synchronous code.

using System.Threading.Tasks;

// db and api are stand-ins for real services
static async Task<int> FetchUserAge(int id) {
    var user = await db.GetUserAsync(id);        // I/O — method suspends here
    var profile = await api.GetProfileAsync(user);
    return profile.Age;
}

At each await, if the task isn't finished yet, the method SUSPENDS: control returns to the caller, and the rest of the method is scheduled to continue when the awaited task completes. No thread sits parked. Under the hood the compiler rewrites the method into a state machine — that's why the async keyword exists: it opts the method into that transformation.

The rules

  • An async method returns Task (no result), Task<T> (a result of type T), or — only for event handlers — void.
  • await someTask unwraps the task: awaiting a Task<int> yields an int. If the task failed, await re-throws the exception, so ordinary try/catch works.
  • await is only legal inside a method marked async.
  • return 42; inside an async Task<int> method — the compiler wraps the value into the task for you.

Trap 1: async void

An async void method can't be awaited, and its exceptions can't be caught by the caller. The entry point matters too: this course's exercises run static async Task Main() (supported by the Mono compiler behind the grader). If you change it to async void Main, the process can exit before your awaited work finishes — and print nothing.

Trap 2: blocking on tasks

int age = FetchUserAge(42).Result;   // BAD — blocks, can deadlock
int age = await FetchUserAge(42);    // right

.Result and .Wait() block the calling thread until the task completes. In UI or classic ASP.NET contexts that's a classic deadlock: the task's continuation needs the very thread you just froze. Inside async code, always await. (The "Tasks in Parallel and Cancellation" lesson later in this course digs into the deadlock mechanics and ConfigureAwait.)

Sequential vs parallel awaiting

var w1 = await GetWeatherAsync("NYC");     // waits for NYC...
var w2 = await GetWeatherAsync("London");  // ...THEN starts London

var t1 = GetWeatherAsync("NYC");           // both in flight already
var t2 = GetWeatherAsync("London");
var both = await Task.WhenAll(t1, t2);     // total time = max, not sum

Awaiting one-by-one silently serializes independent work. Start the tasks first, then await them — the parallel-tasks lesson covers orchestration in depth.

Your exercise

Implement static async Task<int> SumAsync(int[] nums): first await Task.Delay(10); (a stand-in for real I/O), then return the sum — nums.Sum() works (System.Linq is already imported), or write a plain foreach loop. Main reads space-separated integers and prints the awaited result.

For input 1 2 3 4 5 the grader expects exactly:

15

Bare number, nothing else. The grader-caught mistakes:

  1. Leaving the starter's placeholder return 0; in place — every test fails, printing 0 instead of 15, 60, 42.
  2. Changing Main to async void — the process can exit during Task.Delay(10) and print nothing at all.
  3. Adding a label — sum: 15 fails. It's Console.WriteLine(total): the number alone on its line.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…