简体   繁体   中英

Different behavior of exception with async method

Supposed you have 2 async method define as bellow:

public async Task<TResult> SomeMethod1()
{
    throw  new Exception();
}

public async Task<TResult> SomeMethod2()
{
    await Task.Delay(50);
    throw new Exception();
}

Now if you await on those 2 methods the behavior will be pretty much the same. But if you are getting the task the behavior is different.

If I want to cache the result of such a computation but only when the task run to completion. I have to take care of the 2 situation:

First Situation:

public Task<TResult> CachingThis1(Func<Task<TResult>> doSomthing1)
{
    try
    {
       var futur = doSomthing1()
       futur.ContinueWith(
               t =>
               {
                  // ... Add To my cache
               },
               TaskContinuationOptions.NotOnFaulted);
    }
    catch  ()
    {
        // ... Remove from the pending cache
        throw;
    }
}

Second Situation

public Task<TResult> CachingThis2(Func<Task<TResult>> doSomthing)
{
    var futur = SomeMethod2();
    futur.ContinueWith(
       t =>
       {
           // ... Add To my cache
       },
       TaskContinuationOptions.NotOnFaulted);
    futur.ContinueWith(
       t =>
       {
          // ... Remove from the pending cache
       },
       TaskContinuationOptions.OnlyOnFaulted);
}

Now I pass to my caching system the method that will execute the computation to cache.

cachingSystem.CachingThis1(SomeMethod1);
cachingSystem.CachingThis2(SomeMethod2);

Clearly I need to duplicate code in the " ConinueWith on faulted" and the catch block. Do you know if there is a way to make the exception behave the same whether it is before or after an await?

There's no difference in the exception handling required for both SomeMethod1 and SomeMethod2 . They run exactly the same way and the exception would be stored in the returned task.

This can easily be seen in this example;

static void Main(string[] args)
{
    try
    {
        var task = SomeMethod1();
    }
    catch
    {
        // Unreachable code
    }
}

public static async Task SomeMethod1()
{
    throw new Exception();
}

No exception would be handled in this case since the returned task is not awaited.

There is however a distinction between a simple Task -returning method and an async method:

public static Task TaskReturning()
{
    throw new Exception();
    return Task.Delay(1000);
}

public static async Task Async()
{
    throw new Exception();
    await Task.Delay(1000);
}

You can avoid code duplication by simply having an async wrapper method that both invokes the method and await s the returned task inside a single try-catch block:

public static async Task HandleAsync()
{
    try
    {
        await TaskReturning();
        // Add to cache.
    }
    catch
    {
        // handle exception from both the synchronous and asynchronous parts.
    }
}

In addition to what I3arnon said in his answer, in case you ContinueWith on async method without the TaskContinuationOptions you specify, exception captured by the Task parameter you receive in the continuation handler can be handled in the following way:

SomeMethod1().ContinueWith(ProcessResult);
SomeMethod2().ContinueWith(ProcessResult);

With ProcessResult handler which looks like:

private void ProcessResult<TResult>(Task<TResult> task)
{
    if (task.IsFaulted)
    {
        //remove from cahe
    }
    else if (task.IsCompleted)
    {
        //add to cache
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM