Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Better way to show error messages in async methods

The fact that we can't use the await keyword in catch blocks makes it quite awkward to show error messages from async methods in WinRT, since the MessageDialog API is asynchronous. Ideally I would like be able to write this:

    private async Task DoSomethingAsync()
    {
        try
        {
            // Some code that can throw an exception
            ...
        }
        catch (Exception ex)
        {
            var dialog = new MessageDialog("Something went wrong!");
            await dialog.ShowAsync();
        }
    }

But instead I have to write it like this:

    private async Task DoSomethingAsync()
    {
        bool error = false;
        try
        {
            // Some code that can throw an exception
            ...
        }
        catch (Exception ex)
        {
            error = true;
        }

        if (error)
        {
            var dialog = new MessageDialog("Something went wrong!");
            await dialog.ShowAsync();
        }
    }

All methods that need to do this have to follow a similar pattern, which I really don't like, because it reduces the code readability.

Is there a better way to handle this?


EDIT: I came up with this (which is similar to what svick suggested in his comments):

static class Async
{
    public static async Task Try(Func<Task> asyncAction)
    {
        await asyncAction();
    }

    public static async Task Catch<TException>(this Task task, Func<TException, Task> handleExceptionAsync, bool rethrow = false)
        where TException : Exception
    {
        TException exception = null;
        try
        {           
            await task;
        }
        catch (TException ex)
        {
            exception = ex;
        }

        if (exception != null)
        {
            await handleExceptionAsync(exception);
            if (rethrow)
                ExceptionDispatchInfo.Capture(exception).Throw();
        }
    }
}

Usage:

private async Task DoSomethingAsync()
{
    await Async.Try(async () => 
    {
        // Some code that can throw an exception
        ...
    })
    .Catch<Exception>(async ex =>
    {
        var dialog = new MessageDialog("Something went wrong!");
        await dialog.ShowAsync();
    });
}

.Catch<...> calls can be chained to mimick multiple catch blocks.

But I'm not really happy with this solution; the syntax is even more awkward than before...

like image 423
Thomas Levesque Avatar asked Aug 19 '13 09:08

Thomas Levesque


1 Answers

you already have that functionality in TPL

        await Task.Run(async () =>
        {
            // Some code that can throw an exception
            ...
        }).ContinueWith(async (a) =>
        {
            if (a.IsFaulted)
            {
                var dialog = new MessageDialog("Something went wrong!\nError: "
                           + a.Exception.Message);
                await dialog.ShowAsync();
            }
            else
            {
                var dialog2 = new MessageDialog("Everything is OK: " + a.Result);
                await dialog2.ShowAsync();
            }
        }).Unwrap();

In this machine I don't have Windows 8 so I tested in Windows 7 but I think is the same. *Edit as stated in the comments its needed .Unwrap(); in the end for the await to work

like image 160
Pedro.The.Kid Avatar answered Sep 21 '22 15:09

Pedro.The.Kid