Async exception not caught

2019-07-31 22:47发布

Here's my async method for communicating with server:

public static Task<bool> ValidateEmail(string email)
    {
        var url = ServerBase + Resources + Authorization + "check_existence";
        var queryString = SerializationHelper.CreateQueryString(new Dictionary<object, object> {{"email", email}});
        try
        {
            return
                HttpHelper.PostAsync(url, queryString, null).ContinueWith(
                    json => SerializationHelper.DeserializeValidationResponse(json.Result));
        } catch (Exception e)
        {
            return TaskErrorHelper.Error<bool>(e);
        }
    }

An exception thrown while serializing server response (from DeserializeValidationResponse method) isn't caught. What am I doing wrong?

UPD: TaskErrorHelper.Error code:

internal static Task<T> Error<T>(Exception e)
    {
        var tcs = new TaskCompletionSource<T>();
        tcs.SetException(e);
        return tcs.Task;
    }

1条回答
等我变得足够好
2楼-- · 2019-07-31 23:33

An exception thrown while serializing server response (from DeserializeValidationResponse method) isn't caught. What am I doing wrong?

You're not doing anything wrong. What's wrong is your belief that the exception handler has anything to do with the continuation. Let's leave continuations out of it for a moment and just consider this:

class C
{
  object obj = null;
  Action action;
  void M()
  {
    N();
    action();
  }
  void N()
  {
     try
     {
       action = ()=>{Console.WriteLine(obj.ToString());};
     }
     catch (Exception ex) 
     { 
       Console.WriteLine("caught!");
     }
  }

Is it your belief that the catch handler ought to catch the exception thrown by action() just because action happened to be created on a stack frame that had a handler?

That's not how exceptions work.

Your situation is just a more complicated version of this little program. The continuation delegate isn't run until long after the exception handler is gone. Heck, the continuation might not even be run on the same thread!

So how do you get the exception? If the continuation throws an exception then it will be caught automatically and the exception will be stored in the task. You can then pull it out of the task.

Or, you could rewrite your program to put a copy of the handler in the continuation:

public static Task<bool> ValidateEmail(string email)
{
    var url = ...
    var queryString = ...
    try {
        return HttpHelper.PostAsync(url, queryString, null).ContinueWith(
        json => { try { ... } catch(Exception) { ... } });
    } catch( ...

Or, if you use async-await in C# 5, you do get some joy:

public static async Task<bool> ValidateEmail(string email)
{
    var url = ...
    var queryString = ...
    try
    {
        HttpResponseMessage json = await HttpHelper.PostAsync(url, queryString, null);
        SerializationHelper.DeserializeValidationResponse(json.Result));
    } 
    catch (Exception e)
    {
        return false;
    }
    return true;
}

Now the compiler rewrites your code so that it does what you want. The benefit of await is that you don't have to write any of this crazy "continue with" logic; the compiler does it for you.

查看更多
登录 后发表回答