Search code examples
c#.netexceptiontask-parallel-librarytask

Ignore the Tasks throwing Exceptions at Task.WhenAll and get only the completed results


I am working on a Task parallel problem that I have many Tasks that may or may not throw Exception.

I want to process all the tasks that finishes properly and log the rest. The Task.WhenAll propage the Task exception without allowing me to gather the rest results.

    static readonly Task<string> NormalTask1 = Task.FromResult("Task result 1");
    static readonly Task<string> NormalTask2 = Task.FromResult("Task result 2");
    static readonly Task<string> ExceptionTk = Task.FromException<string>(new Exception("Bad Task"));
    var results = await Task.WhenAll(new []{ NormalTask1,NormalTask2,ExceptionTk});

The Task.WhenAll with throw the Exception of ExcceptionTk ignoring the rest results. How I can get the results ignoring the Exception and log the exception at same time?

I could wrap the task into another task that try{...}catch(){...} the internal exception but I don't have access to them and I hope I will not have to add this overhead.


Solution

  • You can create a method like this to use instead of Task.WhenAll:

    public Task<ResultOrException<T>[]> WhenAllOrException<T>(IEnumerable<Task<T>> tasks)
    {    
        return Task.WhenAll(
            tasks.Select(
                task => task.ContinueWith(
                    t => t.IsFaulted
                        ? new ResultOrException<T>(t.Exception)
                        : new ResultOrException<T>(t.Result))));
    }
    
    
    public class ResultOrException<T>
    {
        public ResultOrException(T result)
        {
            IsSuccess = true;
            Result = result;
        }
    
        public ResultOrException(Exception ex)
        {
            IsSuccess = false;
            Exception = ex;
        }
    
        public bool IsSuccess { get; }
        public T Result { get; }
        public Exception Exception { get; }
    }
    

    Then you can check each result to see if it was successful or not.


    EDIT: the code above doesn't handle cancellation; here's an alternative implementation:

    public Task<ResultOrException<T>[]> WhenAllOrException<T>(IEnumerable<Task<T>> tasks)
    {    
        return Task.WhenAll(tasks.Select(task => WrapResultOrException(task)));
    }
    
    private async Task<ResultOrException<T>> WrapResultOrException<T>(Task<T> task)
    {
        try
        {           
            var result = await task;
            return new ResultOrException<T>(result);
        }
        catch (Exception ex)
        {
            return new ResultOrException<T>(ex);
        }
    }