1

I'm having hard time trying to figure out how to properly handle exceptions within async functions. Having the following code:

static async Task NotifyPartyAsync(Uri uri, string paramName, string paramValue)
{
    string link = string.Format("{0}?{1}={2}", uri, paramName, HttpUtility.UrlEncode(paramValue));
    using (var client = new HttpClient())
        try {
            using (HttpResponseMessage response = await client.GetAsync(link, HttpCompletionOption.ResponseHeadersRead))
                logger.DebugFormat("HTTP {0} from {1}", (int)response.StatusCode, link);
        }
        catch (Exception ex)    {
            logger.Error(ex);
        }
}

static void NotifyParty(Uri uri, string paramName, string paramValue)
{
    string link = string.Format("{0}?{1}={2}", uri, paramName, HttpUtility.UrlEncode(paramValue));
    using (var client = new HttpClient())
        try {
            using (HttpResponseMessage response = client.GetAsync(link, HttpCompletionOption.ResponseHeadersRead).Result)
                logger.DebugFormat("HTTP {0} from {1}", (int)response.StatusCode, link);
        }
        catch (Exception ex)    {
            logger.Error(ex);
        }
}

How do I refactor it to not repeat 99% code common to both functions? I need to notify an uri both synchronously and asynchronously and I don't care about the result except I want it logged.

UserControl
  • 14,766
  • 20
  • 100
  • 187
  • 2
    If you have both an asynchronous *and* synchronous API, then you will end up with a lot of duplicate code. Hopefully this is only a temporary situation as your code is becoming async. – Stephen Cleary Mar 25 '14 at 12:15

3 Answers3

1

You should look at this question: Good pattern for exception handling when using async calls. It might be what you're looking for.

Community
  • 1
  • 1
pepo
  • 8,644
  • 2
  • 27
  • 42
1

Basically, with async/await you can follow the same pattern you do for synchronous programming. Don't handle exceptions inside every async method, unless absolutely required. Handle them on the topmost level, i.e., inside the outermost async or synchronous method. What's this method is depends on the execution environment.

E.g., it might be an async void event handler in case it's a UI app:

async Task DoWorkAsync()
{
    // don't handle exceptions here
}

async void Form_Load(object s, EventArgs args)
{
    try {
        await DoWorkAsync();
    }
    catch (Exception ex) 
    {
        // log
        logger.Error(ex);
        // report
        MessageBox.Show(ex.Message);
    }
}

Or, it may be the Main entry point of a console app:

static void Main(string[] args)
{
    try {
        DoWorkAsync().Wait();
    }
    catch (Exception ex) 
    {
        // log
        logger.Error(ex);
        throw; // re-throw to terminate
    }
}

You should understand though how exceptions get propagated for async methods. Check this for some more details.

Also, not all exceptions are the same and should be handled equally. Check Eric Lippert's "Vexing exceptions".

Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
0
static void NotifyParty(Uri uri, string paramName, string paramValue)
{
  await NotifyPartyAsync(uri, paramName, paramValue);
}

OR

static void NotifyParty(Uri uri, string paramName, string paramValue)
{
  NotifyPartyAsync(uri, paramName, paramValue).Wait();
}

let me know if you're working with .net 4.5 or 4.0. there may be some nuances based on that.

Raja Nadar
  • 9,409
  • 2
  • 32
  • 41
  • It's .net 4.5. Your answer suggests that my async function is written properly but I'm not sure about the line that logs `response.StatusCode` - doesn't it block the function from returning immediately after `GetAsync()` ? – UserControl Mar 25 '14 at 07:34
  • on the await client.GetAsync line, the execution will go back to the caller of the uber async method.. as for the 'HttpResponseMessage response = await client.GetAsync' line, it'll block since you're awaiting the result.. and because of the logging next line, this is an unavoidable blocking. – Raja Nadar Mar 25 '14 at 07:42
  • 1
    for control flow on async/await: http://msdn.microsoft.com/en-us/library/hh191443.aspx#BKMK_WhatHappensUnderstandinganAsyncMethod – Raja Nadar Mar 25 '14 at 07:42
  • `RunSynchronously()` raises 'RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method.' exception. Why? – UserControl Mar 25 '14 at 08:09
  • aah yes.. with the async/await model, you need to use .Wait () instead of RunSynchronously() – Raja Nadar Mar 25 '14 at 08:23