# Async Programming

Course: [C# Advanced](https://www.devtutorials.ai/learn/advanced-csharp) · Section: Asynchronous Programming
Source: https://www.devtutorials.ai/learn/advanced-csharp/asynchronous-programming/async-programming

> Wait without freezing: Task, Task<T>, async and await, how await really runs, return types, ValueTask, async void, exceptions, and async file and network calls.

## Introduction to Async Programming

Imagine you are at a busy café ordering coffee.

You place your order. The person at the counter does not just stand and watch the coffee machine until your drink is ready. Instead, they take more orders and help other customers. When your coffee is ready, they call your name.

This is **asynchronous** work. You start something that takes time, do other things while you wait, and come back when it is finished.

**In simple words:** async programming lets your program **wait without freezing**.

Most slow work in real programs is about waiting, not calculating:

- waiting for a web server to respond,
- waiting for a file to load from disk,
- waiting for a database to send data.

C# gives you three main tools for this: `Task`, `async`, and `await`. This lesson will explain all three, step by step.

**Easy Way to Remember:** a `Task` is like a **receipt** for work that will finish later. `await` means "I will wait here until this receipt is ready".

## What You Will Learn

1. The difference between synchronous and asynchronous code.
2. Why async exists, and when it helps.
3. `Task` and `Task<T>`, and how to create and start them.
4. The `async` and `await` keywords, and what really happens at an `await`.
5. Which return types an async method can have, and when to use `ValueTask`.
6. How to design async methods, and why `async void` is dangerous.
7. "Start first, await later"—the trick that makes waits overlap.
8. How exceptions work in async code.
9. Async file and network operations.

**About the Run buttons in this lesson.** The examples run in your browser on .NET in WebAssembly. Code that reads real files or calls real web servers cannot run there, so those examples are shown as plain code. The runnable cells use `Task.Delay` to stand in for the slow part, which behaves exactly the same way.

## Start With a Working Example

Press **Run**.

```csharp runnable title="Make tea while the water boils"
using System;
using System.Threading.Tasks;

Console.WriteLine("Put the kettle on");
Task<string> boiling = BoilWaterAsync();

Console.WriteLine("Get a cup");
Console.WriteLine("Add a tea bag");

string water = await boiling;
Console.WriteLine($"Pour the {water}");
Console.WriteLine("Tea is ready");

static async Task<string> BoilWaterAsync()
{
    await Task.Delay(500); // the slow part: 0.5 seconds
    return "hot water";
}
```

The kettle starts first. While it heats, the program gets the cup and the tea bag. Only when it really needs the water does it `await` it.

### Try This

Move the line `string water = await boiling;` up, so it comes straight after the kettle starts. The output is the same, but now the program waits before getting the cup. This shows the difference between async code that overlaps work and async code that does not.

## Synchronous vs Asynchronous Programming

**Synchronous** code does one thing at a time. Each line must finish before the next line starts. If a line is slow, everything after it must wait.

**Asynchronous** code can start a slow operation and let the program keep going. The result is collected later.

Think of two ways to do laundry:

- **Synchronous:** start the washing machine, then stand in front of it for an hour.
- **Asynchronous:** start the washing machine, cook dinner, and come back when it beeps.

| | Synchronous | Asynchronous |
|---|---|---|
| While waiting | The thread is blocked and does nothing | The thread is free for other work |
| Typical method | `string Read()` | `Task<string> ReadAsync()` |
| How you get the result | It is returned directly | You `await` the task |
| Good for | Quick calculations | Waiting for disk, network, database, timers |

Here is the same "two downloads" job written both ways. The synchronous version waits for each one in turn. The async version starts both, then waits once.

```csharp runnable title="One after another, or both at once"
using System;
using System.Threading.Tasks;

Console.WriteLine("Async, one after another:");
string a = await DownloadAsync("photo.jpg", 300);
string b = await DownloadAsync("music.mp3", 400);
Console.WriteLine($"Got {a} and {b}");

Console.WriteLine();
Console.WriteLine("Async, both at once:");
Task<string> first = DownloadAsync("photo.jpg", 300);
Task<string> second = DownloadAsync("music.mp3", 400);
Console.WriteLine($"Got {await first} and {await second}");

static async Task<string> DownloadAsync(string name, int ms)
{
    Console.WriteLine($"  start {name}");
    await Task.Delay(ms);
    Console.WriteLine($"  done  {name}");
    return name;
}
```

In the first half, the second download only starts after the first one is done. In the second half, both start straight away, so their waits overlap. Waits of 300 ms and 400 ms take about 400 ms in total instead of 700 ms.

## Why Async Exists

A **thread** is a worker that runs your code. Every program has a limited number of threads.

When a thread waits for something slow with synchronous code, it is **blocked**. It cannot do anything else, even though it is not working.

This causes two main problems:

- **Frozen apps.** In a desktop or mobile app, one thread draws the screen and handles clicks. If it is blocked for two seconds while a file downloads, the window freezes and shows "Not responding".
- **Wasted servers.** A web server gives each request a thread. If every thread is blocked waiting for the database, new visitors have to wait, even though the CPU is almost idle.

Async fixes both. While the slow operation runs, the thread is **given back** and can do other work. When the result arrives, the method continues where it stopped.

**In simple words:** async does not make slow work faster. It stops your program from wasting time while it waits.

Async helps when your program is **waiting** (for disk, network, a database, or a timer). It does not make a heavy calculation run faster. A loop that adds up a billion numbers is just as slow with `async` in front of it.

## Task

A **`Task`** is an object that represents work that is running now, or will finish later.

Think of a parcel tracking number. The parcel is not here yet, but the tracking number lets you check on it and know when it arrives.

A `Task` can tell you:

| Property | What it tells you |
|---|---|
| `IsCompleted` | The work has finished (in any way) |
| `IsCompletedSuccessfully` | The work finished without an error |
| `IsFaulted` | The work failed with an exception |
| `IsCanceled` | The work was cancelled |
| `Status` | The current state, such as `Running` or `RanToCompletion` |

A plain `Task` does not carry a result. Use it for work that just needs to **finish**, like saving a file or sending an email.

```csharp runnable title="A Task with no result"
using System;
using System.Threading.Tasks;

Task saving = SaveReportAsync();
Console.WriteLine($"Finished yet? {saving.IsCompleted}");

await saving;
Console.WriteLine($"Finished now? {saving.IsCompleted}");
Console.WriteLine($"Status: {saving.Status}");

static async Task SaveReportAsync()
{
    await Task.Delay(200);
    Console.WriteLine("Report saved");
}
```

Right after the call, the task is still running, so `IsCompleted` is `False`. After the `await`, it has finished.

## Task&lt;T&gt;

**`Task<T>`** is a task that finishes with a **result** of type `T`.

- `Task<int>` finishes with an `int`.
- `Task<string>` finishes with a `string`.
- `Task<List<Order>>` finishes with a list of orders.

When you `await` a `Task<T>`, you get the `T` back. The task is the box, and `await` opens it.

```csharp runnable title="await opens the box and hands you the value"
using System;
using System.Threading.Tasks;

Task<int> countTask = CountVisitorsAsync();
int visitors = await countTask;
Console.WriteLine($"Visitors today: {visitors}");

Task<string> nameTask = GetCityAsync();
Console.WriteLine($"City: {await nameTask}");

static async Task<int> CountVisitorsAsync()
{
    await Task.Delay(100);
    return 128; // an int, not a Task<int>
}

static async Task<string> GetCityAsync()
{
    await Task.Delay(100);
    return "Chennai";
}
```

Inside an `async Task<int>` method, you `return 128;`, a plain `int`. The compiler wraps it in the task for you.

## Creating and Running Tasks

There are four common ways to get a task. You will use the first one most of the time.

| Way | When to use it |
|---|---|
| Call an async method: `DownloadAsync()` | Almost always. Most .NET I/O methods already return tasks |
| `Task.Run(() => ...)` | Move CPU-heavy work off the current thread |
| `Task.FromResult(value)` | You already have the answer, but the method must return a task |
| `Task.CompletedTask` | A `Task` method that has nothing to wait for |

```csharp runnable title="Four ways to get a task"
using System;
using System.Threading.Tasks;

// 1. Call an async method. It starts running straight away.
Task<string> fromMethod = LoadGreetingAsync();

// 2. Task.Run: run a piece of work as a task.
Task<int> fromRun = Task.Run(() =>
{
    int total = 0;
    for (int i = 1; i <= 100; i++) total += i;
    return total;
});

// 3. Task.FromResult: a task that is already finished, with a value.
Task<int> ready = Task.FromResult(7);

// 4. Task.CompletedTask: an already-finished task with no value.
Task nothingToDo = Task.CompletedTask;

Console.WriteLine(await fromMethod);
Console.WriteLine($"Sum 1..100 = {await fromRun}");
Console.WriteLine($"Ready value = {await ready}");
await nothingToDo;
Console.WriteLine($"FromResult finished at once? {ready.IsCompleted}");

static async Task<string> LoadGreetingAsync()
{
    await Task.Delay(100);
    return "Hello from an async method";
}
```

You rarely need `new Task(...)` plus `.Start()`. A task made that way does nothing until you start it, which is a common source of programs that "hang" forever. Call an async method or use `Task.Run` instead.

## async Keyword

The **`async`** keyword goes in front of a method's return type:

```text
static async Task<string> LoadNameAsync()
```

It does two things:

1. It **allows** you to use `await` inside the method.
2. It tells the compiler to **wrap** the method's return value in a task. You write `return "Asha";` and the caller receives a `Task<string>`.

`async` by itself does **not** make anything run in the background. A method marked `async` with no `await` inside runs exactly like a normal method, and the compiler warns you about it (warning `CS1998`).

By convention, an async method's name ends with **`Async`**: `LoadNameAsync`, `SaveAsync`, `SendEmailAsync`. This helps callers see that they should `await` it.

**Easy Way to Remember:** `async` is a **permission slip**. It allows `await`, but does nothing on its own.

## await Keyword

**`await`** means: "if this task is not finished yet, pause this method here, give the thread back, and continue from this line when the task is done."

`await` does three jobs:

- It **waits** for the task, without blocking the thread.
- It **unwraps** the result: `await` on a `Task<int>` gives you an `int`.
- It **rethrows** any exception the task failed with, so a normal `try`/`catch` can handle it.

```csharp runnable title="await waits, unwraps, and keeps the order"
using System;
using System.Threading.Tasks;

Console.WriteLine("Checking stock...");
int stock = await GetStockAsync("Pen");
Console.WriteLine($"Pens in stock: {stock}");

if (stock < 50)
{
    Console.WriteLine("Ordering more pens...");
    int ordered = await OrderAsync("Pen", 100);
    Console.WriteLine($"Ordered {ordered} pens");
}

static async Task<int> GetStockAsync(string item)
{
    await Task.Delay(150);
    return 30;
}

static async Task<int> OrderAsync(string item, int amount)
{
    await Task.Delay(150);
    return amount;
}
```

The code reads top to bottom like normal code. That is the main point of `await`: async code that looks like regular code.

## Execution Flow of await

What really happens at an `await`? Follow the numbers in this program.

```csharp runnable title="Who runs when?"
using System;
using System.Threading.Tasks;

Console.WriteLine("1. Main: calling CookAsync");
Task cooking = CookAsync();
Console.WriteLine("4. Main: CookAsync gave back a task, Main carries on");
await cooking;
Console.WriteLine("7. Main: cooking is finished");

static async Task CookAsync()
{
    Console.WriteLine("2. CookAsync: starts running at once");
    Console.WriteLine("3. CookAsync: reaches await, pauses, returns a task");
    await Task.Delay(300);
    Console.WriteLine("5. CookAsync: resumes after the delay");
    Console.WriteLine("6. CookAsync: done");
}
```

Step by step:

1. Calling `CookAsync()` runs its code **straight away**, on the same thread, like any method.
2. At `await Task.Delay(300)` the delay is not finished, so `CookAsync` **pauses** and **returns an unfinished task** to its caller.
3. The caller carries on (line 4) until it reaches its own `await`.
4. When the delay ends, `CookAsync` **resumes** from the line after its `await`.
5. When `CookAsync` finishes, its task completes, and `Main` resumes after `await cooking`.

If the awaited task is **already finished**, `await` does not pause at all. It just takes the result and keeps going.

An async method runs synchronously until its first `await` on something unfinished. Code before that first `await` runs immediately when you call the method, not later.

## Async Method Return Types

An async method can return one of these types:

| Return type | Use it when | The caller gets |
|---|---|---|
| `Task` | The method does work but has no result | Something to `await` |
| `Task<T>` | The method produces a value | A `T` after `await` |
| `ValueTask` / `ValueTask<T>` | The method often finishes without waiting (see the next section) | The same as `Task` / `Task<T>` |
| `void` | **Only** for event handlers, such as a button click | Nothing to await (see "Avoiding async void") |
| `IAsyncEnumerable<T>` | The method returns many values over time | Values with `await foreach` (lesson 3) |

```csharp runnable title="Task and Task<T> side by side"
using System;
using System.Threading.Tasks;

await LogAsync("Starting");          // Task: nothing comes back
double price = await GetPriceAsync(); // Task<double>: a value comes back
await LogAsync($"Price is {price:0.00}");

static async Task LogAsync(string message)
{
    await Task.Delay(50);
    Console.WriteLine($"[log] {message}");
}

static async Task<double> GetPriceAsync()
{
    await Task.Delay(50);
    return 249.5;
}
```

## Task vs ValueTask

Every `Task` is an object. Creating it uses a small amount of memory.

Usually that does not matter. But some methods are called millions of times and **usually already have the answer**—for example, a method that reads from a cache and only goes to the database when the cache is empty.

**`ValueTask<T>`** avoids creating a `Task` object when the result is already available. When the method does have to wait, it behaves like a normal task.

```csharp runnable title="ValueTask: no extra object when the answer is cached"
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

var cache = new Dictionary<int, string>();

Console.WriteLine(await GetUserAsync(1)); // not cached: waits
Console.WriteLine(await GetUserAsync(1)); // cached: no waiting
Console.WriteLine(await GetUserAsync(2)); // not cached: waits

async ValueTask<string> GetUserAsync(int id)
{
    if (cache.TryGetValue(id, out string? name))
        return $"{name} (from cache)";

    await Task.Delay(100); // pretend database call
    name = id == 1 ? "Asha" : "Ravi";
    cache[id] = name;
    return $"{name} (from database)";
}
```

| | `Task<T>` | `ValueTask<T>` |
|---|---|---|
| Memory | Always an object | No object when the result is ready at once |
| Can be awaited more than once | Yes | **No**, await it only once |
| Can be stored and awaited later | Yes | Avoid it |
| Default choice | **Yes** | Only for hot paths that usually finish at once |

**Easy Way to Remember:** use `Task` by default. Use `ValueTask` only if you have measured and found a real performance problem.

## Async Method Design

Good async methods follow a few simple rules:

1. **End the name with `Async`.** For example, `GetOrdersAsync`, not `GetOrders`.
2. **Return `Task` or `Task<T>`.** Not `void`.
3. **Go async all the way.** If a method awaits something, it should itself be async, and so should its caller, up to `Main` or the event handler.
4. **Do not block.** Never call `.Result` or `.Wait()` on an unfinished task. Use `await`.
5. **Keep CPU work and waiting work separate.** Use `Task.Run` for heavy calculation, and the real async APIs for I/O.

```csharp runnable title="Async all the way up"
using System;
using System.Threading.Tasks;

// Main awaits the top method...
decimal total = await GetCartTotalAsync(cartId: 42);
Console.WriteLine($"Cart total: {total}");

// ...which awaits the methods below it.
static async Task<decimal> GetCartTotalAsync(int cartId)
{
    int[] itemIds = await GetItemIdsAsync(cartId);
    decimal total = 0;
    foreach (int id in itemIds)
        total += await GetPriceAsync(id);
    return total;
}

static async Task<int[]> GetItemIdsAsync(int cartId)
{
    await Task.Delay(50);
    return new[] { 1, 2, 3 };
}

static async Task<decimal> GetPriceAsync(int itemId)
{
    await Task.Delay(50);
    return itemId * 100m;
}
```

Blocking on a task might seem easier because it avoids changing the caller:

```text
// Don't do this
decimal total = GetCartTotalAsync(42).Result;
GetCartTotalAsync(42).Wait();
```

In desktop and older ASP.NET apps, this can **deadlock**: the program freezes forever. In this browser playground, it fails at once with "Cannot wait on monitors on this runtime". Use `await` instead.

## Avoiding async void

An `async void` method returns **nothing**. The caller gets no task, so it:

- **cannot wait** for the method to finish,
- **cannot catch** its exceptions. An exception inside an `async void` method can crash the whole program.

```text
// Don't do this
static async void SaveAsync()
{
    await Task.Delay(100);
    throw new InvalidOperationException("Disk full");
}

try
{
    SaveAsync();           // no task to await
}
catch (Exception ex)       // never reached: the exception escapes and crashes the app
{
    Console.WriteLine(ex.Message);
}
```

The fix is simple: return `Task` instead of `void`, and `await` the call:

```csharp runnable title="Return Task, and the error can be caught"
using System;
using System.Threading.Tasks;

try
{
    await SaveAsync();
}
catch (InvalidOperationException ex)
{
    Console.WriteLine($"Save failed: {ex.Message}");
}
Console.WriteLine("The program is still running");

static async Task SaveAsync()
{
    await Task.Delay(100);
    throw new InvalidOperationException("Disk full");
}
```

The **only** place `async void` is acceptable is for **event handlers**, such as a button's `Click` method, because its signature must return `void`. Even there, put a `try`/`catch` around the body.

## Start First, Await Later

Calling an async method **starts** the work. The `await` is only where you collect the result.

So when two operations do not depend on each other, start **both**, then await **both**. Their waits overlap.

```csharp runnable title="Breakfast: one by one, or all at once"
using System;
using System.Threading.Tasks;

Console.WriteLine("--- One by one ---");
await MakeAsync("toast", 300);
await MakeAsync("eggs", 300);
await MakeAsync("coffee", 300);

Console.WriteLine("--- All at once ---");
Task toast = MakeAsync("toast", 300);
Task eggs = MakeAsync("eggs", 400);
Task coffee = MakeAsync("coffee", 200);
await toast;
await eggs;
await coffee;
Console.WriteLine("Breakfast is ready");

static async Task MakeAsync(string food, int ms)
{
    Console.WriteLine($"Start {food}");
    await Task.Delay(ms);
    Console.WriteLine($"{food} ready");
}
```

One by one takes about 900 ms. All at once takes about 400 ms, the time of the slowest item. The coffee finishes first because it is the quickest, even though it was started last.

Lesson 2 shows `Task.WhenAll`, which awaits a whole group of tasks in one line.

## Exception Handling in Async Code

When an async method throws, the exception does not come out immediately. It is **stored inside the task**, and the task becomes **faulted**.

The exception is thrown again at the **`await`**. So an ordinary `try`/`catch` around the `await` works, just like in synchronous code.

```csharp runnable title="The exception travels inside the task"
using System;
using System.Threading.Tasks;

Task<decimal> payment = ChargeAsync(-50);
Console.WriteLine("ChargeAsync was called; nothing has been thrown yet");

try
{
    decimal charged = await payment;
    Console.WriteLine($"Charged {charged}");
}
catch (ArgumentException ex)
{
    Console.WriteLine($"Caught at the await: {ex.Message}");
}

Console.WriteLine($"Task faulted? {payment.IsFaulted}");

static async Task<decimal> ChargeAsync(decimal amount)
{
    await Task.Delay(100);
    if (amount <= 0)
        throw new ArgumentException("Amount must be positive");
    return amount;
}
```

Two rules follow from this:

- **Always await your tasks.** A task that is never awaited keeps its exception to itself, and the error is silently lost.
- **`finally` works too.** Use it to clean up whether the operation succeeded or failed.

```csharp runnable title="try, catch and finally with await"
using System;
using System.Threading.Tasks;

await UploadAsync("report.pdf", fail: false);
await UploadAsync("huge-video.mp4", fail: true);

static async Task UploadAsync(string file, bool fail)
{
    Console.WriteLine($"Uploading {file}");
    try
    {
        await Task.Delay(100);
        if (fail) throw new InvalidOperationException("File too large");
        Console.WriteLine("  Upload complete");
    }
    catch (InvalidOperationException ex)
    {
        Console.WriteLine($"  Upload failed: {ex.Message}");
    }
    finally
    {
        Console.WriteLine("  Connection closed");
    }
}
```

## Async File Operations

Reading and writing files is a classic place for async. The disk is slow compared with the CPU, so the thread should not sit waiting for it.

.NET has an async version of every common file method:

| Synchronous | Asynchronous |
|---|---|
| `File.ReadAllText` | `File.ReadAllTextAsync` |
| `File.WriteAllText` | `File.WriteAllTextAsync` |
| `File.ReadAllLines` | `File.ReadAllLinesAsync` |
| `File.AppendAllText` | `File.AppendAllTextAsync` |
| `StreamReader.ReadLine` | `StreamReader.ReadLineAsync` |
| `StreamWriter.WriteLine` | `StreamWriter.WriteLineAsync` |

This program writes a file and reads it back. Run it on your own computer, because the browser cannot reach your disk.

```text
using System;
using System.IO;
using System.Threading.Tasks;

string path = "notes.txt";

await File.WriteAllTextAsync(path, "Buy milk\nCall Ravi\n");
await File.AppendAllTextAsync(path, "Pay rent\n");

string[] lines = await File.ReadAllLinesAsync(path);
Console.WriteLine($"The file has {lines.Length} lines:");
foreach (string line in lines)
    Console.WriteLine($"- {line}");
```

Output:

```text
The file has 3 lines:
- Buy milk
- Call Ravi
- Pay rent
```

The stream classes work the same way on any kind of stream. Here they read and write a `MemoryStream`, a stream that lives in memory, so it runs in the browser too:

```csharp runnable title="Async writing and reading with streams"
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;

using var memory = new MemoryStream();

using (var writer = new StreamWriter(memory, Encoding.UTF8, leaveOpen: true))
{
    await writer.WriteLineAsync("Buy milk");
    await writer.WriteLineAsync("Call Ravi");
    await writer.WriteLineAsync("Pay rent");
}

memory.Position = 0; // go back to the start before reading

using var reader = new StreamReader(memory);
int number = 1;
string? line;
while ((line = await reader.ReadLineAsync()) != null)
{
    Console.WriteLine($"{number}. {line}");
    number++;
}
```

Swap the `MemoryStream` for `File.OpenRead("notes.txt")` and the same loop reads a real file line by line.

## Async Network Operations

Network calls are often the slowest thing a program does. A web request can take hundreds of milliseconds, and the thread should never sit blocked for that long.

**`HttpClient`** is the .NET class for web requests, and all of its methods are async.

```text
using System;
using System.Net.Http;
using System.Threading.Tasks;

using var client = new HttpClient();

string html = await client.GetStringAsync("https://example.com");
Console.WriteLine($"Downloaded {html.Length} characters");

HttpResponseMessage response = await client.GetAsync("https://example.com/missing");
Console.WriteLine($"Status: {(int)response.StatusCode}");
```

This needs a real internet connection and a server that allows the request, so it is shown as plain code. Run it in a console app on your own computer.

| Method | What it returns |
|---|---|
| `GetStringAsync(url)` | The body as a `string` |
| `GetByteArrayAsync(url)` | The body as bytes, for images and files |
| `GetAsync(url)` | The full response: status code, headers and body |
| `PostAsync(url, content)` | Sends data and returns the response |

The pattern is always the same, so you can practise it with a pretend service. Here, `Task.Delay` stands in for the time the network takes:

```csharp runnable title="Calling a (pretend) weather service"
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;

var weather = new FakeWeatherService();

foreach (string city in new[] { "Chennai", "Delhi", "Atlantis" })
{
    try
    {
        int temperature = await weather.GetTemperatureAsync(city);
        Console.WriteLine($"{city}: {temperature} °C");
    }
    catch (HttpRequestException ex)
    {
        Console.WriteLine($"{city}: request failed ({ex.Message})");
    }
}

class FakeWeatherService
{
    private readonly Dictionary<string, int> _data = new()
    {
        ["Chennai"] = 33,
        ["Delhi"] = 28,
    };

    public async Task<int> GetTemperatureAsync(string city)
    {
        await Task.Delay(150); // pretend network time
        if (!_data.TryGetValue(city, out int temperature))
            throw new HttpRequestException("404 Not Found");
        return temperature;
    }
}
```

`HttpClient` throws an `HttpRequestException` when a request fails, so real code catches it the same way.

## Common mistakes

| Mistake | Wrong | Correct | Why |
|---|---|---|---|
| Forgetting to await | `SaveAsync();` | `await SaveAsync();` | The program moves on early and errors are lost |
| Blocking on a task | `GetNameAsync().Result` | `await GetNameAsync()` | Blocks the thread and can freeze the app |
| Using async void | `async void SendAsync()` | `async Task SendAsync()` | Errors cannot be caught and can crash the app |
| Awaiting independent work one by one | `await A(); await B();` | Start A and B, then await both | The waits add up instead of overlapping |
| Sleeping in async code | `Thread.Sleep(1000);` | `await Task.Delay(1000);` | Thread.Sleep blocks the thread |
| `async` with no `await` | `async Task<int> Get() => 5;` | `Task.FromResult(5)` | Runs synchronously; warning CS1998 |

## What to Remember

- Async lets a program **wait without freezing**. It helps with waiting, not with heavy calculation.
- A `Task` is work that finishes later. `Task<T>` finishes with a value of type `T`.
- `async` allows `await` and wraps the return value in a task. On its own it does nothing.
- `await` waits without blocking, unwraps the result, and rethrows any exception.
- An async method runs normally until its first `await` on unfinished work, then returns a task to its caller.
- Return `Task` or `Task<T>`. Use `async void` only for event handlers.
- Use `ValueTask` only for hot methods that usually finish at once.
- Start independent work first, await it later, so the waits overlap.
- Exceptions are stored in the task and thrown at the `await`, so `try`/`catch` works as normal.
- Use the `...Async` versions of file and network methods, and never block with `.Result`, `.Wait()`, or `Thread.Sleep`.

Next: Task Coordination
