Tuesday, May 14, 2013

Async, Exceptions and Library Design

Hat tip to Stephen Toub for discussing this with me and helping to describe the solution.

At my CodeMash precompiler, I mentioned how the C# compiler ensures that methods marked with the 'async' keyword that contain 'await' expressions never throw synchronous exceptions. Instead, those methods will return a Task (or Task<T>) that will be placed in the faulted state if the method throws an exception. The compiler does the work to add the appropriate try/catch clauses to your methods, and translates any exceptions thrown during your method's execution into returned faulted task.

Jon Skeet was concerned by this strategy. He felt that API designers would prefer throwing synchronous exceptions for obvious programmer errors (things like ArgumentNullException and so on). I mentioned that the language specification defines that async methods will returned faulted tasks; they will not throw synchronous exceptions.

Of course, there is a way around this. You have to separate your public async APIs into two pieces: A public synchronous API that does parameter validation and state validation. This synchronous method then calls an internal async method that does the asynchronous work.

As an example, consider this (rather contrived) async method:
            
public async Task<string> FizzBuzzAsync(int val)
{
    if (val <= 0)
        throw new ArgumentException("We can't fizzbuzz negative numbers, or 0");

   await Task.Delay(250);
 
   var rVal = string.Empty;
    if (val % 3 == 0)
        rVal += "Fizz";

    if (val % 5 == 0)
        rVal += "Buzz";

    if (string.IsNullOrWhiteSpace(rVal))
        rVal = val.ToString();

    return rVal;
}

Read more: BillWagner
QR: Inline image 1