Why exactly is emptiness asynchronous?

So, I understand why returning void from async usually doesn't make any sense, but I ran into a situation where I think it would be perfectly correct. 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 simpler and more general. Can someone please explain to me why this is terrible code and also how can I change it to follow the rules correctly?

Thanks for any help.

+3


source to share


2 answers


Ok, following the reasons in the "avoid async void

" article
:

  • Asynchronous void methods have different error handling semantics. Exceptions from PrimeCustomTask

    will be very inconvenient to handle.
  • Asynchronous methods have different semantics. This is a maintainability and code reuse oriented argument. In fact, the logic is in PrimeCustomTask

    there and that it is - it cannot be compiled into a higher level async

    .
  • Asynchronous invalid methods are difficult to test. Naturally from the first two points it is very difficult to write a unit test coverage PrimeCustomTask

    (or whatever calls it).

It's also important to note that async Task

is a 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 in terms of language design.



The reason async void

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

:

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

private async Task PrimeCustomTask()

      

Then the disadvantages are async void

limited to your event handler. In particular, exceptions from PrimeCustomTask

naturally propagate to its (asynchronous) callers ( OnLoad

), PrimeCustomTask

can be composed (naturally called by other asynchronous methods), and PrimeCustomTask

much easier to include in a unit test.

+9


source


The use of void async is only usually considered "bad" because:

  • You can't wait for it to complete (as mentioned in this post)
  • Any unhandled exceptions will terminate your process (ouch!)


There are many cases (for example, yours) where this is good. Be careful when using.

+4


source







All Articles