async void Methods In C# – The Dangers That You Need to Know
Tags: csharp, coding, programming, dotnet
Published: 2024-03-15 16:00:00

The post [async void Methods In C# – The Dangers That You Need to Know](https://www.devleader.ca/2024/03/07/async-void-methods-in-c-the-dangers-that-you-need-to-know/) appeared first on [Dev Leader](https://www.devleader.ca).

`async void` methods in C# are a source of a lot of problems for many developers getting into [writing async await code](https://www.devleader.ca/2024/02/27/async-await-in-c-3-beginner-tips-you-need-to-know/). The pattern that we’re suggested to use is of course `async Task`, but there are cases — like with Event Handlers in C# — where the method signatures just aren’t compatible.

In this article, I’ll explain why `async void` methods in C# are something you want to avoid. We’ll cover some code examples that compare `async void` and `async Task` for better understanding, and I’ll also explain what you should do if you have no other choice but `async void`.

---

## What Are async void Methods in CSharp?

In C#, `async void` methods are a way to define asynchronous methods that don’t return a value. These methods are typically used for event handlers or other scenarios where the method signature being enforced does not support a `Task` return type and instead `void` is enforced.

`async void` methods are defined by using the `async` keyword before the method signature, followed by the return type `void`. For example:

```csharp
public async void SomeMethod()
{
    // Code here
}
```

When compared to regular asynchronous methods that return a `Task` or `Task<T>`, there are a few important differences to note. And when I say “important” I mean “You really need to avoid this as much as you humanly can so you save yourself some headaches”.

{% embed https://weekly.devleader.ca/embed %}

### Differences Between async void And async Task in CSharp

One of the main differences between `async void` methods and `async Task` methods lies in how exceptions are handled.

In `async Task` methods, any exceptions that occur are captured by the returned `Task` object. This allows the calling code to handle the exception or await the `Task` to observe any exceptions later. This is how the entire async await infrastructure has been built in C#. It’s often why you’ll see async await get introduced into a codebase and then all of the calling code starts getting converted over to also be async await — You really DO want it there.

On the other hand, `async void` methods cannot be awaited directly, and any exceptions that occur within them will be bubbled up to… where? The SynchronizationContext that started the async method in the first place. Even [Stephen Cleary from Microsoft mentions in his article](https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming):

> With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started. **Figure 2** illustrates that exceptions thrown from async void methods can’t be caught naturally.
> 
> [**Stephen Cleary**](https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming)

### Code Example of async Task vs async void Methods in CSharp

It’ll be helpful to compare two variations of the same layout of code using each of these patterns to see how the issues can arise. Consider the following example that uses async Task:

```csharp
public async Task ProcessDataAsync()
{
    // Some asynchronous operations
}

public async void HandleButtonClick(object sender, EventArgs e)
{
    try
    {
        await ProcessDataAsync();
    }
    catch (Exception ex)
    {
        // Handle the exception
    }
}
```

In this code, if an exception occurs in the `ProcessDataAsync` event handler method, it will be caught by the try-catch block in the `HandleButtonClick` method and can be appropriately handled. However, if the `ProcessDataAsync` method is defined as `` `async void` `` instead, any exceptions thrown by `ProcessDataAsync` would bypass the catch block in the `HandleButtonClick` event handler method and potentially crash the application:

```csharp
public async void ProcessDataAsync()
{
    // Some asynchronous operations
}

public async void HandleButtonClick(object sender, EventArgs e)
{
    try
    {
        ProcessDataAsync();
    }
    catch (Exception ex)
    {
        // This will never catch the async exceptions!
    }
}
```

---

## The Dangers of async void Methods in CSharp

The common theme that you’re hopefully noticing in this article is that `async void` methods in C# are dangerous and something you should be trying to avoid. Here’s a list of challenges that we get with `async void` methods, to hopefully steer you away from using them (unless you have no choice):

1. **Error Propagation**: `async void` methods do not allow errors to be caught or propagated. When an exception occurs within such a method, it escapes to the synchronization context, often resulting in an unhandled exception that can crash the application.
    
2. **Awaiting Behavior**: Unlike `async Task` methods, `async void` methods cannot be awaited. This can lead to issues with controlling the flow of asynchronous operations, potentially causing race conditions or executing operations out of the intended order.
    
3. **Debugging Difficulty**: Debugging exceptions in `async void` methods is more challenging because the call stack may not accurately represent the execution flow at the time the exception was thrown, complicating the process of identifying and fixing bugs.
    

---

## Best Practices for Handling async void Methods in CSharp

[When working with async void methods in C#, it is important to be aware of their potential dangers](https://www.devleader.ca/2023/01/27/async-void-how-to-tame-the-asynchronous-nightmare/) and follow best practices to ensure a robust and reliable codebase. Here are some recommendations for handling async void methods cautiously:

1. Avoid `async void` whenever possible: `async void` methods should generally be avoided, especially in scenarios where exception handling and error recovery are critical. [While async void methods may appear convenient, they lack the ability to propagate exceptions and can result in unpredictable program behavior](https://www.devleader.ca/2023/01/27/async-void-how-to-tame-the-asynchronous-nightmare/). Instead, consider using `async Task` methods, which provide better error-handling capabilities.
    
2. Use `async Task` instead: By using the `async Task` return type for asynchronous methods, you can take advantage of the Task’s built-in exception handling mechanism. This enables you to catch and handle exceptions appropriately, ensuring that your code maintains control over the execution flow. Using `async Task` methods also allows for better code maintainability, testability, and fewer mysterious problems than `async void`.
    
3. Handle exceptions in `async void` methods: If you must use `async void` methods, it is important to properly handle exceptions to prevent them from silently propagating and causing unexpected system behavior. One way to achieve [this is by enclosing the code within a try/catch block](https://www.devleader.ca/2023/02/14/async-eventhandlers-a-simple-safety-net-to-the-rescue/). Within the catch block, you can log the exception and handle it accordingly, such as displaying an error message to the user or rolling back any relevant operations.
    
4. Log and monitor asynchronous operations: When working with `async void` methods, logging and monitoring become even more critical. Since these methods do not have a return type, it becomes challenging to determine their completion or identify any potential issues. Implementing a robust logging and monitoring system, such as using a logging framework like Serilog or utilizing application insights, can help track the progress and status of your asynchronous operations, aiding in debugging and troubleshooting.
    

### Try/Catch for async void Methods in CSharp

[I’ve written about several different ways to try working with this kind of code before](https://www.devleader.ca/2023/02/14/async-eventhandlers-a-simple-safety-net-to-the-rescue/), but ultimately it feels like making sure you wrap every `async void` method body in try/catch is the most straightforward. Maybe someone can create a Rosyln analyzer to enforce this?

Here’s an example demonstrating how you can use a try-catch around the entire body of code that is `async void`:

```csharp
public async void DoSomethingAsync()
{
    try
    {
        // Perform asynchronous operations
        await Task.Delay(1000);
        await SomeAsyncMethod();
    }
    catch (Exception ex)
    {
        // TODO: ... whatever you need to do to properly report
        // on issues in your async void calls so that you
        // can debug them more effectively.

        // Log the exception and handle it appropriately
        Logger.Error(ex, "An error occurred while executing DoSomethingAsync");
        // Display an error message or take necessary action
        DisplayErrorMessage("Oops! Something went wrong. Please try again later.");
    }
}
```

By adhering to these best practices, you can mitigate the risks associated with `async void` methods and avoid a pile of neverending headaches. Remember to prioritize using `async Task` methods whenever possible for better exception handling and control over the asynchronous execution flow — you *really* don’t want to add `async void` anywhere unless it’s an absolute last resort.

---

## Wrapping up async void Methods in CSharp

In conclusion, `async void` methods in C# can be dangerous for several reasons, and you’ll want to prioritize NOT using them. There are unfortunate situations where APIs and method signatures do not line up, such as Event Handlers, but otherwise, do your best to avoid these.

By using `async void`, [we lose the ability to await or handle exceptions properly](https://www.devleader.ca/2023/01/27/async-void-how-to-tame-the-asynchronous-nightmare/). This can lead to unhandled exceptions and unexpected behavior in our code. To avoid these hazards, I recommend that you use `async Task` for methods that are intended to be asynchronous wherever you possibly can. This allows us to await the result, handle exceptions, and have better control over our code’s execution flow. It promotes better error handling and improves overall code quality. When you have no other choice, [make sure you are wrapping your entire async void method in a try/catch](https://www.devleader.ca/2023/02/14/async-eventhandlers-a-simple-safety-net-to-the-rescue/) and investing in proper error handling/logging/reporting.

Approach async programming with caution and continuously seek to enhance your understanding of the topic! If you found this useful and you’re looking for more learning opportunities, consider [subscribing to my free weekly software engineering newsletter](https://subscribe.devleader.ca) and check out my [free videos on YouTube](https://www.youtube.com/@devleader?sub_confirmation=1)! [Check out my Discord community](https://www.devleader.ca/discord-community-access/) if you want to connect with me and other software engineers on these types of topics!

{% embed https://weekly.devleader.ca/embed %}

---

## **Want More Dev Leader Content?**

* Follow along on this platform if you haven’t already!    
* Subscribe to my free weekly software engineering and dotnet-focused newsletter. I include exclusive articles and early access to videos:  
    [**SUBSCRIBE FOR FREE**](https://subscribe.devleader.ca/)    
* Looking for courses? Check out my offerings:  
    [**VIEW COURSES**](https://devleader.ca/courses)    
* E-Books & other resources:  
    [**VIEW RESOURCES**](https://products.devleader.ca/)    
* Watch hundreds of full-length videos on my YouTube channel:  
    [**VISIT CHANNEL**](https://youtube.com/@devleader?sub_confirmation=1)    
* Visit my website for hundreds of articles on various software engineering topics (including code snippets):  
    [**VISIT WEBSITE**](https://devleader.ca/)    
* Check out the repository with many code examples from my articles and videos on GitHub:  
    [**VIEW REPOSITORY**](https://github.com/ncosentino/DevLeader)