.NET
async
await
programming
concurrency

.NET asyncawait fundamentals

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In .NET, async and await are the main tools for writing asynchronous code that stays readable. They let code look sequential while still allowing the runtime to release the thread during waits such as network calls, file I/O, or timers.

What async and await Actually Mean

An async method usually returns Task or Task<T>. Inside that method, await pauses execution until the awaited operation finishes:

csharp
1using System.Net.Http;
2using System.Threading.Tasks;
3
4static async Task<string> FetchAsync()
5{
6    using var client = new HttpClient();
7    return await client.GetStringAsync("https://example.com");
8}

The method looks sequential, but it does not block the thread while the HTTP call is in progress.

Task Is the Asynchronous Result Container

Task represents the ongoing asynchronous work:

  • 'Task means no result value'
  • 'Task<int> means the operation eventually produces an int'

Example:

csharp
1using System.Net.Http;
2using System.Threading.Tasks;
3
4static async Task<int> CountBytesAsync()
5{
6    using var client = new HttpClient();
7    var data = await client.GetByteArrayAsync("https://example.com");
8    return data.Length;
9}

Without Task, there would be no standard object for completion, exceptions, and composition.

async Does Not Automatically Mean “Another Thread”

This is one of the most important fundamentals. async does not automatically run your code on a different thread, and it does not make CPU-bound work finish faster by itself.

It helps most when the work spends time waiting on:

  • network I/O
  • file I/O
  • database operations
  • timers

If the problem is CPU-heavy computation, that is a different concurrency question.

End-to-End Example

Here is a simple file example:

csharp
1using System;
2using System.IO;
3using System.Threading.Tasks;
4
5class Program
6{
7    static async Task Main()
8    {
9        string path = "message.txt";
10
11        await File.WriteAllTextAsync(path, "Hello async world");
12        string content = await File.ReadAllTextAsync(path);
13
14        Console.WriteLine(content);
15    }
16}

The code reads top to bottom, but the I/O operations still happen asynchronously.

Exceptions Still Use try and catch

One reason async and await are so useful is that exception handling remains familiar:

csharp
1try
2{
3    string text = await File.ReadAllTextAsync("missing.txt");
4    Console.WriteLine(text);
5}
6catch (IOException ex)
7{
8    Console.WriteLine(ex.Message);
9}

You do not need a callback-style error model just because the operation is asynchronous.

Return Types to Use

As a practical rule:

  • use Task for async methods with no result
  • use Task<T> for async methods with a result
  • use async void only for event handlers

Example event handler:

csharp
1private async void Button_Click(object sender, EventArgs e)
2{
3    await Task.Delay(500);
4}

Outside UI or framework event handlers, async void makes exception handling and composition much harder, so avoid it.

Avoid Blocking Async Code

A classic mistake is calling .Result or .Wait() on a task:

csharp
// Avoid this
string text = FetchAsync().Result;

That can block threads unnecessarily and in some environments can cause deadlock patterns. Prefer awaiting tasks all the way through the call chain when possible.

Composition Is Where Async Becomes Powerful

Asynchronous code becomes especially useful when you compose operations:

csharp
1Task<string> a = FetchAsync();
2Task<string> b = FetchAsync();
3
4string[] results = await Task.WhenAll(a, b);

This lets multiple I/O-bound operations make progress without writing manual callback coordination logic.

Common Pitfalls

  • Calling an async method but forgetting to await it.
  • Blocking with .Result or .Wait() instead of keeping the code async.
  • Assuming async automatically parallelizes CPU-bound code.
  • Using async void for normal methods.
  • Mixing synchronous and asynchronous APIs in a way that removes the benefit of non-blocking flow.

Summary

  • 'async and await make asynchronous .NET code readable and composable.'
  • They work best for I/O-bound operations rather than CPU-bound work.
  • 'Task and Task<T> represent asynchronous operations and their eventual results.'
  • Exception handling still uses normal try and catch.
  • Avoid blocking async code and prefer awaiting tasks through the full call chain.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.