How to return AggregateException from async method

后端 未结 3 1385
野的像风
野的像风 2021-01-24 17:06

I got an async method working like an enhanced Task.WhenAll. It takes a bunch of tasks and returns when all are completed.

public async Task MyWhenA         


        
3条回答
  •  谎友^
    谎友^ (楼主)
    2021-01-24 17:18

    Use a TaskCompletionSource.

    The outermost exception is created by .Wait() or .Result - this is documented as wrapping the exception stored inside the Task inside an AggregateException (to preserve its stack trace - this was introduced before ExceptionDispatchInfo was created).

    However, Task can actually contain many exceptions. When this is the case, .Wait() and .Result will throw an AggregateException which contains multiple InnerExceptions. You can access this functionality through TaskCompletionSource.SetException(IEnumerable exceptions).

    So you do not want to create your own AggregateException. Set multiple exceptions on the Task, and let .Wait() and .Result create that AggregateException for you.

    So:

    var tcs = new TaskCompletionSource();
    tcs.SetException(new[] { t1.Exception, t2.Exception });
    return tcs.Task;
    
    
    

    Of course, if you then call await MyWhenAll(..) or MyWhenAll(..).GetAwaiter().GetResult(), then it will only throw the first exception. This matches the behaviour of Task.WhenAll.

    This means you need to pass tcs.Task up as your method's return value, which means your method can't be async. You end up doing ugly things like this (adjusting the sample code from your question):

    public static Task MyWhenAll(Task t1, Task t2)
    {
        var tcs = new TaskCompletionSource();
        var _ = Impl();
        return tcs.Task;
    
        async Task Impl()
        {
            await Task.Delay(10);
            try
            {
                await Task.WhenAll(t1, t2);
                tcs.SetResult(null);
            }
            catch
            {
                tcs.SetException(new[] { t1.Exception, t2.Exception });
            }
        }
    }
    
    
    

    At this point, though, I'd start to query why you're trying to do this, and why you can't use the Task returned from Task.WhenAll directly.

    提交回复
    热议问题