Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why exactly is void async bad?

Tags:

c#

async-await

So I understand why returning void from async would normally make no sense, but I've ran into a situation where I think it would be perfectly valid. Consider the following contrived example:

protected override void OnLoad(EventArgs e)
{
    if (CustomTask == null)
        // Do not await anything, let OnLoad return.
        PrimeCustomTask();
}
private TaskCompletionSource<int> CustomTask;

// I DO NOT care about the return value from this. So why is void bad?
private async void PrimeCustomTask()
{
    CustomTask = new TaskCompletionSource<int>();
    int result = 0;
    try
    {
        // Wait for button click to set the value, but do not block the UI.
        result = await CustomTask.Task;
    }
    catch
    {
        // Handle exceptions
    }
    CustomTask = null;

    // Show the value
    MessageBox.Show(result.ToString());
}

private void button1_Click(object sender, EventArgs e)
{
    if (CustomTask != null)
        CustomTask.SetResult(500);
}

I realize this is an unusual example, but I tried to make it simple and more generalized. Could someone explain to me why this is horrible code, and also how I could modify it to follow conventions correctly?

Thanks for any help.

like image 743
AnotherProgrammer Avatar asked Aug 01 '17 21:08

AnotherProgrammer


People also ask

Can async void be awaited?

For methods other than event handlers that don't return a value, you should return a Task instead, because an async method that returns void can't be awaited. Any caller of such a method must continue to completion without waiting for the called async method to finish.

What is the difference between async void and async task?

A Task returning async method can be awaited, and when the task completes, the continuation of the task is scheduled to run. A void returning async method cannot be awaited; it is a "fire and forget" method. It does work asynchronously, and you have no way of telling when it is done.

How does async void work?

async void has the same semantics as async Task , except for exceptions. An async void method will capture the current SynchronizationContext at the beginning of the method, and any exceptions from that method will be captured and raised directly on that captured context.

Can we use async void in C#?

In theory, C# developers working with async/await do not need to use or implement callbacks, but occasionally you may need to work with a library that does not make use of async/await and instead requires you to provide a callback method.


2 Answers

Well, walking through the reasons in the "avoid async void" article:

  • Async void methods have different error-handling semantics. Exceptions escaping from PrimeCustomTask will be very awkward to handle.
  • Async void methods have different composing semantics. This is an argument centered around code maintainability and reuse. Essentially, the logic in PrimeCustomTask is there and that's it - it can't be composed into a higher-level async method.
  • Async void methods are difficult to test. Following naturally from the first two points, it's very difficult to write a unit test covering PrimeCustomTask (or anything that calls it).

It's also important to note that async Task is the natural approach. Of the several languages that have adopted async/await, C#/VB are the only ones AFAIK that support async void at all. F# doesn't, Python doesn't, JavaScript and TypeScript don't. async void is unnatural from a language design perspective.

The reason async void was added to C#/VB was to enable asynchronous event handlers. If you change your code to use async void event handlers:

protected override async void OnLoad(EventArgs e) {   if (CustomTask == null)     await PrimeCustomTask(); }  private async Task PrimeCustomTask() 

Then the async void disadvantages are restricted to your event handler. In particular, exceptions from PrimeCustomTask are propagated naturally to its (asynchronous) callers (OnLoad), PrimeCustomTask can be composed (called naturally from other asynchronous methods), and PrimeCustomTask is much easier to include in a unit test.

like image 75
Stephen Cleary Avatar answered Sep 26 '22 16:09

Stephen Cleary


Using void async is only generally seen as "bad" because:

  • You can’t wait for its completion (as mentioned in this post already
  • Is especially painful if it is called by a parent thread that exits before it has completed
  • Any unhandled exceptions will terminate your process (ouch!)

There are plenty of cases (like yours) where using it is fine. Just be cautious when using it.

like image 24
elf Avatar answered Sep 22 '22 16:09

elf