Async/Await Deep Dive
Advanced
25 min read
Lesson 4 of 6
async/await lets your code run long operations
(database calls, file I/O, web requests) without blocking the thread
that's running it — crucial for responsive UIs and scalable web servers.
A synchronous vs asynchronous method
// Synchronous — blocks the calling thread until it finishes
public string ReadFileSync(string path)
{
return File.ReadAllText(path);
}
// Asynchronous — frees the thread while waiting on I/O
public async Task<string> ReadFileAsync(string path)
{
return await File.ReadAllTextAsync(path);
}
The rules
- An
async method returns Task, Task<T>, or void (only for event handlers).
await can only be used inside an async method.
- Async "all the way" — once you go async, calling code should await it too.
Async database call
public async Task<List<Student>> GetStudentsAsync()
{
return await _context.Students
.Where(s => s.EnrolledOn.Year == 2024)
.ToListAsync();
}
// In a Razor Pages handler:
public async Task OnGetAsync()
{
Students = await _studentService.GetStudentsAsync();
}
Running tasks in parallel
Task<List<Student>> studentsTask = GetStudentsAsync();
Task<List<Course>> coursesTask = GetCoursesAsync();
await Task.WhenAll(studentsTask, coursesTask);
var students = studentsTask.Result;
var courses = coursesTask.Result;
Common mistake: blocking on async code
// DON'T DO THIS — can cause deadlocks
var students = GetStudentsAsync().Result;
// DO THIS instead
var students = await GetStudentsAsync();
Exception handling with async
try
{
var students = await GetStudentsAsync();
}
catch (SqlException ex)
{
// handle database-specific failure
}
catch (Exception ex)
{
// handle anything else
}
Key Takeaway
Async/await makes your application more responsive and scalable by freeing threads during I/O operations.