0

The common description of why async void is part of C# is for event handlers. For example:

private async void button_Click(object sender, RoutedEventArgs e)
{
    using (var httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync("http://example.com");
        var content = await response.Content.ReadAsStringAsync();
        this.textBox.Text = content;
    }
}

I find this reason unsatisfying, as this kind of event handler can be written without async void like this:

private void button_Click(object sender, RoutedEventArgs e)
{
    button_ClickAsync().ForgetTask();
}

private async Task button_ClickAsync()
{
    using (var httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync("http://example.com");
        var content = await response.Content.ReadAsStringAsync();
        this.textBox.Text = content;
    }
}

static class TaskExtensions { public static void ForgetTask(this Task task) { } }

Why isn't the latter good enough? Why is async void a necessary part of C#? What problem can't be solved without async void?

Jay Bazuzi
  • 45,157
  • 15
  • 111
  • 168
  • It wont compile using `async void` but it will with `async Task` instead. – mrogal.ski Jan 13 '17 at 16:11
  • 2
    It have different exception propagation behavior. – user4003407 Jan 13 '17 at 16:12
  • @m.rogalski did you mean that to be the other way round? – stuartd Jan 13 '17 at 16:12
  • Direct support for asynchronous event handlers is more satisfying and less error prone than a hand-coded fire-and-forget extension – Panagiotis Kanavos Jan 13 '17 at 16:13
  • 1
    PetSerAl is right. See [this answer](http://stackoverflow.com/a/12144426/464709) for more information. – Frédéric Hamidi Jan 13 '17 at 16:13
  • @PanagiotisKanavos: `async void` is not fire-and-forget (they can block until they return, if they never `await Task.Yield()`) and that is no different from `async Task`. And `async void` is error prone in a bunch of other cases, the worst being `async` lambdas passed to methods that take an `Action`. – Jay Bazuzi Jan 13 '17 at 16:19
  • @stuartd: No one is suggest changing the return type of C# events. – Jay Bazuzi Jan 13 '17 at 16:20
  • 1
    Possible duplicate of [async/await - when to return a Task vs void?](http://stackoverflow.com/questions/12144077/async-await-when-to-return-a-task-vs-void) – Dour High Arch Jan 13 '17 at 16:20
  • If I understand right, it's neither C# nor async/await problem. Windows form and Wpf require the return type `void` for all button events. That means, you cannot set the event returns `Task` or any types. If you use it in Console or Asp.net, returning `void` is not required and should be avoided. – Tân Jan 13 '17 at 16:42
  • @TânNguyễn If you read my code example, you can see that `button_Click` can be used as an event handler. – Jay Bazuzi Jan 14 '17 at 15:46

3 Answers3

2

As you showed yourself, it's not necessary. You can write a functionally identical program to one that uses it without using it. It's useful insofar as there simply are times where you really do want to create an async method that doesn't expose any way of observing the result, such as in the situation you mentioned. Could they have designed the feature in such a way that users could accomplish that another way, yes, you showed one possible way, the C# language designers choose another.

Servy
  • 202,030
  • 26
  • 332
  • 449
1

Exceptions from an unawaited async Task method will be unobserved, firing the TaskScheduler.UnobservedTaskException event. For example:

    static void Main()
    {
        TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs args) => { Console.WriteLine(args.Exception.InnerException.Message + " unobserved"); };
        try
        {
            ThrowExceptionInAsyncTask();
            Console.WriteLine("ThrowExceptionInAsyncTask not caught");
        }
        catch (Exception)
        {
            Console.WriteLine("ThrowExceptionInAsyncTask caught");
        }
        GC.Collect();
        try
        {
            ThrowExceptionInAsyncVoid();
            Console.WriteLine("ThrowExceptionInAsyncVoid not caught");
        }
        catch (Exception)
        {
            Console.WriteLine("ThrowExceptionInAsyncVoid caught");
        }
        GC.Collect();
    }

    static async Task ThrowExceptionInAsyncTask()
    {
        throw new InvalidOperationException("ThrowExceptionInAsyncTask");
    }

    static async void ThrowExceptionInAsyncVoid()
    {
        throw new InvalidOperationException("ThrowExceptionInAsyncVoid");
    }

Produces:

ThrowExceptionInAsyncTask not caught
ThrowExceptionInAsyncVoid not caught
ThrowExceptionInAsyncTask unobserved
Jay Bazuzi
  • 45,157
  • 15
  • 111
  • 168
  • 1
    Note that in C# 5.0 this behavior didn't exist, and the two options were identical, so the idea that `async void` is *necessary* for this behavior is false. Additionally, the fact that your approach for handling an asynchronous event handler has slightly different error handling semantics in the current version doesn't mean that one couldn't write such a solution without `async void`, particularly from a language designer's point of view (they most certainly *could* implement a `ForgetTask` method with the same error handling semantics). – Servy Jan 13 '17 at 16:50
0

It is used in two cases as in my own knowledge:

  1. In event handlers. Because event handlers cannot have a return type.
  2. In a method intended to have no return type, and not awaited.

What you did here is a good workaround, but think of it as flexibility in language to make life easier for developers and simplify language syntax. Same example like yours is: Why System.Linq exists in C#?! Why we can use syntax like myIntArray.Max() while we can iterate through the array and find the maximum value!

That doesn't mean there is no other reason, but I'm sharing my thoughts and I hope that helps.

Ahmed Abdelkarim
  • 277
  • 2
  • 10