Should I Avoid 'Async Void' Event Handlers

Should I avoid 'async void' event handlers?

The guideline is to avoid async void except when used in an event handler, so using async void in an event handler is OK.

That said, for unit testing reasons I often like to factor out the logic of all async void methods. E.g.,

public async Task OnFormLoadAsync(object sender, EventArgs e)
{
await Task.Delay(2000);
...
}

private async void Form_Load(object sender, EventArgs e)
{
await OnFormLoadAsync(sender, e);
}

async void event handlers - clarification?

The articles you link to make the reasons pretty clear. Don't use it because it isn't reliable beyond the most basic of scenarios. There is only so much async tracking trickery we can pull in the synchronization context on async void methods. We did work to make those basic scenarios work, but our general guidance is to avoid using them and instead explicitly register async work.

How to handle the case that the progeam needs to exit before an async void event handler finishes?

You can subscribe Window.Closing event and set e.Cancel to true if async event handler is running.

public partial class MainWindow : Window
{
//Flag indicating async operation is in progress
private bool m_EventHandlerIsRunning = false;

public MainWindow()
{
InitializeComponent();
}

// Subscribe this method to MainWindow's Closing event
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
// Forbid MainWindow to close, if async event handler is running
e.Cancel = m_EventHandlerIsRunning;
}

// You will have to include following in all async event handlers
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
// Forbid MainWindow to be closed
m_EventHandlerIsRunning = true;

// Do something async
await Task.Delay(1000);
}
finally
{
// Let MainWindow to be closed
m_EventHandlerIsRunning = false;
}
}
}

But your application must be able to recover from such situations (where clean-up code is not executed) anyway, because there is no way for you to prevent OS crash or power outage, that can occur at anytime.

One line fire and forget: void vs. async void + await


My understanding is that adding async void and await in this case would just be useless overhead.

No.

If Handler returned a Task, then that would be true and eliding async/await would be fine; the code without async/await would just return the Task directly instead of "unwrapping" it with await and "wrapping" it back into a Task with async.

However, that's not the case here; Handler returns void, so the code without async/await will just ignore the returned Task, which is wrong the vast majority of the time (hence the compiler warning). Specifically, ignoring the Task will ignore any exceptions from that Task. It's also not possible for your code to know when an ignored Task has completed, but presumably that's acceptable since your handler is returning void.

There is a "registration" that async void methods do so that the framework is aware there is a task still in progress, so the framework knows when it's safe to shut down. The only .NET provided framework that actually cares about that is ASP.NET pre-Core; all other .NET-provided frameworks (including all UI frameworks) ignore that "registration".

Use cases for async void methods, revisited


How does the Framework keep track of pending async void methods, including event handlers?

The framework doesn't do anything special to keep track of async void methods. They're just like any other async method.

Also, your method either has a proper signature or it doesn't; event handlers do not care and have no logic to detect or work with async specifically.

A custom scheduler would be able to keep track of running tasks, but not have any specific knowledge if one is from an async void method or not. I don't think this is the right solution anyway -- if you find yourself needing to keep track of an async void method, you need to rethink your design.

Are they any useful for fire-and-forget logging scenarios? I think they actually may be, as long as the correct time-stamp is preserved

I don't know what you mean by a timestamp?

Async void is fine for any method where the caller will either never need to care about the result of the method call, or where it is being notified of the result elsewhere. These cases should be very exceedingly rare.

Fire-and-forget might be one such scenario, though I feel people often misuse fire-and-forget and end up just hiding important bugs from themselves.



Related Topics



Leave a reply



Submit