-2

I'm currently working on a project in a team of more than 2 people. Each person on the team has varying levels of skill and knowledge with .NET, particularly the async/await keywords for asynchronous programming in C# 5. I personally have learned much about this paradigm and am familiar with some of the intricacies and gotchas of it, but some of the team are not. Has anybody out there come up with a good convention for annotating / attributing code that returns async Task / async void to indicate whether the result of the call must be awaited or whether it can be left to run "fire and forget" in the background ? The project has a lot of heavy (over?) usage of asynchronous programming. We're working on improving performance at the moment, and there's a lot of instances where the results of functions are async Task that are not awaited and left as fire/forget, and async void where the result should really be a task that gets awaited. I should think that the answer to this should be obvious based on return type, but it's not obvious to everyone on the team.

Alex Marshall
  • 10,162
  • 15
  • 72
  • 117

1 Answers1

2

Has anybody out there come up with a good convention for annotating / attributing code that returns async Task / async void to indicate whether the result of the call must be awaited or whether it can be left to run "fire and forget" in the background ?

async void methods are ment for compatability with event handlers. They are in no way an indicator for "use this in a fire and forget scenario". You should only use them when you want to await on an async method inside, as their exception handling mechanism is different than with async Task or async Task<T>. I suggest reading Best Practices in Asynchronous Programming by Stephan Cleary which has a whole paragraph about the use of async void.

To your question, i am really not a fan of fire and forget methods. If you still want to use them, you should make them return a Task and attach a continuation which takes care of error handling to be on the safe side. Something along the lines of:

public async Task FireAndForget()
{
    await Task.Delay(1000);
    Console.WriteLine("Foo")
}

public static void ForgetSafely(this Task task)
{
     // Handle exceptions here.
}

And use it as:

FireAndForget().ForgetSafely();

Fire and forget shouldn't be a common approach in your codebase. If it is, you're doing something wrong. For those cases when you do need them, simply document your method and state it could be used in a fire and forget way but should handle exceptions properly.

Another thing to keep in mind is that when using async Task instead of async void is that emitting the await keyword will cause the compiler to generate an error, which should make fellow developers in your team to question their use of the method:

warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

For more exploring of async void:

  1. async/await - when to return a Task vs void?
  2. C# Async Tips and Tricks Part 2 : Async Void
Community
  • 1
  • 1
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • Thank you very much for that. Unfortunately my team members have developed a very large codebase with many async anti patterns that's going to require a significant amount of cleanup. We really don't want to change the behaviour of the system because much of the behaviour in place was done to keep the ui of a wpf app responsive – Alex Marshall Jul 12 '14 at 15:28
  • @AlexMarshall Well then a refactor should be done slowley to ensure you guys get on the right track. Running `async void` methods could cause some peculiar exceptions to occur. – Yuval Itzchakov Jul 12 '14 at 16:24