C# Async/await

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.

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


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: