The key difference between async Task
and async void
in C# lies in how they behave, how errors are handled, and when each should be used.
Summary Table
Feature | async Task |
async void |
---|---|---|
Return type | Returns a Task (can await ) |
Returns nothing (void ) |
Can be awaited | ![]() |
![]() |
Exception handling | ![]() try/catch when awaited |
![]() |
Use case | ![]() |
![]() |
Testable / chainable | ![]() |
![]() |
Example: async Task
public async Task DoSomethingAsync()
{
await Task.Delay(1000);
Console.WriteLine("Done");
}
// Usage
await DoSomethingAsync(); // Can be awaited
- You can
await
it. - Exceptions can be caught with
try/catch
.
Example: async void
public async void DoSomething()
{
await Task.Delay(1000);
Console.WriteLine("Done");
}
// Usage
DoSomething(); // Cannot be awaited
- Canβt
await
it. - If an exception occurs, it will bypass
try/catch
and potentially crash the application. - Only appropriate for event handlers like:
button.Click += async (sender, e) => {
await Task.Delay(1000);
Console.WriteLine("Clicked");
};
Rule of Thumb
Use
async Task
for almost everything.
Use
async void
only for event handlers.
Letβs go through real-world examples to show the difference between async Task
and async void
, especially around exception handling and event handlers.
async Task
Example with Exception Handling
public async Task LoadDataAsync()
{
await Task.Delay(500); // Simulate work
throw new InvalidOperationException("Something went wrong");
}
// Caller method
public async Task RunAsync()
{
try
{
await LoadDataAsync(); // Can await
}
catch (Exception ex)
{
Console.WriteLine($"Caught: {ex.Message}"); // Will catch the exception
}
}
Output:
Caught: Something went wrong
Safe and testable.
Exception is caught in
try/catch
because we await
ed the Task
.
async void
Example with Exception Handling (Fails)
public async void LoadData()
{
await Task.Delay(500);
throw new InvalidOperationException("Oops!");
}
public void Run()
{
try
{
LoadData(); // No await here
}
catch (Exception ex)
{
Console.WriteLine($"Caught: {ex.Message}"); // This will NOT run
}
}
Output:
- The app crashes or logs an unhandled exception.
Why?
You canβt await
void
, so the method continues without waiting β and exceptions escape the try/catch
.
async void
for UI Event Handler (Valid Use Case)
private async void Button_Click(object sender, EventArgs e)
{
await Task.Delay(500);
MessageBox.Show("Button clicked!");
}
This is OK:
- Events require
void
return type. async void
is the only choice.- Exceptions still need special handling (e.g. try/catch inside the method).
Exception Handling Inside an async void
Event Handler
private async void Button_Click(object sender, EventArgs e)
{
try
{
await Task.Delay(500);
throw new Exception("Button failed!");
}
catch (Exception ex)
{
MessageBox.Show($"Error: {ex.Message}");
}
}
Best practice: Always use
try/catch
inside async void
event handlers.
TL;DR
Situation | Use |
---|---|
Regular async work | async Task |
Want to await it later | async Task |
Event handler (UI, events) | async void |
Method throws exception | Avoid async void unless itβs an event |