Async Tip: why you should avoid void

2017/03/15

no comments

Async Tip: why you should avoid void

The guideline to prefer async method which return Task over async method that return void is quite known,

but why should you follow the guideline?

Code Snippet
  1. public async void NotRecomendedAsync()
  2. {
  3.     // do something
  4.     await SomeCodeWhichMayThrowAsync();
  5.     // do something
  6. }
  7.  
  8. public async Task RecomendedAsync()
  9. {
  10.     // do something
  11.     await SomeCodeWhichMayThrowAsync();
  12.     // do something
  13. }

Task, Async, Await, Parallel, best practice, guideline

The first obvious reason to use the guideline is testability.

When you return void the test don’t really know when it is right time to

check for the exception.

The other reason (which is less known) is that running async method which return void and

throw unhandled exception, will crash the process.

If the method would return Task the unhandled exception would be swallow (still available

via AppDomain.CurrentDomain.UnhandledException).

Check it using the following code:

Code Snippet
  1. static void Main(string[] args)
  2. {
  3.     //Task t = RecomendedAsync();
  4.     NotRecomendedAsync();
  5.     Console.ReadKey();
  6. }
  7.  
  8. private static async Task RecomendedAsync()
  9. {
  10.     await Task.Delay(100);
  11.     throw new ArgumentException();
  12. }
  13.  
  14. private static async void NotRecomendedAsync()
  15. {
  16.     await Task.Delay(100);
  17.     throw new ArgumentException();
  18. }

When you want to expose on your API, but do async operation within the method.

For example assume that you design a logger API, the caller shouldn’t await the log report

while the method will actually do IO call (either writing to the database or sending the log to Application Insight or ELK).

In order to have implementation which return void which doesn’t crash when unhandled exception thrown you can use the following pattern:

Code Snippet
  1. public void FireAndForget()
  2. {
  3.     Task _ = FireAndForgetAsync();
  4. }
  5.  
  6. private async Task FireAndForgetAsync()
  7. {
  8.     await Task.Delay(100);
  9.     throw new ArgumentException();
  10. }

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*