How to use Async/Await
An async Task
in C# refers to a method that performs asynchronous operations and returns a Task
that represents ongoing work.
Using async
allows the method to asynchronously await the completion of other asynchronous methods without blocking the main thread.
This is particularly useful when working with tasks like I/O operations, database access, or web requests that could otherwise slow down or block an application if run synchronously.
Example:
public async Task ExampleAsync()
{
Task<int> longRunningTaskA = LongRunningTaskA(); // Start the task immediatley
// Wait asynchronously for task A to complete
int resultA = await longRunningTaskA;
Task<int> longRunningTaskB = LongRunningTaskB(); // Now TaskA is complete Start taskB
// Wait asynchronously for task B to complete
int resultB = await longRunningTaskB;
Console.WriteLine($"Result: {resultA}");
Console.WriteLine($"Result: {resultB}");
}
public async Task<int> LongRunningTaskA()
{
await Task.Delay(4000); // Simulate an operation that takes 1 second
return 40;
}
public async Task<int> LongRunningTaskB()
{
await Task.Delay(2000); // Simulate an operation that takes 1 second
return 42;
}
Here's a breakdown of the components:
1. Async Method:
When a method is marked with the async
keyword, it indicates that it contains awaitable asynchronous operations inside. It signals that the method can run asynchronously and may yield control back to the caller at points where await
is used, allowing other work to be done in the meantime.
public async Task ExampleAsyncMethod()
{
// Asynchronously await an operation that takes time (like fetching
In C#, you can wait for an asynchronous task using the await
keyword, which pauses the execution of the method until the task completes.
You can also use .Wait()
or .GetAwaiter().GetResult()
to block the calling thread until the task finishes, but these approaches are less recommended as they can lead to issues like deadlocks, especially in UI or web applications.
1. Using await
(Preferred):
The await
keyword is the most common and recommended way to wait for an asynchronous task to complete. It doesn't block the calling thread; instead, it allows the task to run asynchronously, and the method will resume execution when the awaited task completes.
Example:
public async Task ExampleAsync()
{
Task<int> longRunningTask = LongRunningOperationAsync(); // Start the task
// Wait asynchronously for the task to complete
int result = await longRunningTask;
Console.WriteLine($"Result: {result}");
}
public async Task<int> LongRunningOperationAsync()
{
await Task.Delay(1000); // Simulate an operation that takes 1 second
return 42;
}
2. Using .Wait()
or .Result
(Blocking the Thread):
If you want to block the calling thread until the task completes (which is generally discouraged), you can use .Wait()
or access the .Result
of the task.
Example using .Wait()
:
Task longRunningTask = LongRunningOperationAsync();
longRunningTask.Wait(); // Blocks until the task is complete
Example using .Result
:
Task<int> longRunningTask = LongRunningOperationAsync();
int result = longRunningTask.Result; // Blocks and gets the result
3. Using .GetAwaiter().GetResult()
(Blocking the Thread, Avoiding AggregateException):
This approach blocks the thread like .Result
but avoids wrapping exceptions in AggregateException
.
Example:
Task<int> longRunningTask = LongRunningOperationAsync();
int result = longRunningTask.GetAwaiter().GetResult(); // Blocks and gets the result
When to Use await
:
- Always prefer
await
when writing asynchronous code. It is non-blocking, handles exceptions properly, and avoids deadlocks.
When to Use .Wait()
or .Result
:
- Only use
.Wait()
or .Result
in rare cases when you have no other choice, such as when you're in a non-async context (e.g., synchronous entry point) and need to force waiting on an async operation. But be cautious of potential deadlocks.
Note:
If you call LongRunningTaskA() and dont want LongRunningTaskB(), to execute until LongRunningTaskA() has completed then ensure to call await before the call to LongRunningTaskB()
Example:
public async Task ExampleAsync()
{
Task<int> longRunningTaskA = LongRunningTaskA(); // Start the task
// Wait asynchronously for task A to complete
int resultA = await longRunningTaskA;
Task<int> longRunningTaskB = LongRunningTaskB(); // Start the task
// Wait asynchronously for task B to complete
int resultB = await longRunningTaskB;
Console.WriteLine($"Result: {resultA}");
Console.WriteLine($"Result: {resultB}");
}
public async Task<int> LongRunningTaskA()
{
await Task.Delay(4000); // Simulate an operation that takes 1 second
return 40;
}
public async Task<int> LongRunningTaskB()
{
await Task.Delay(2000); // Simulate an operation that takes 1 second
return 42;
}