How to run multiple tasks, handle exceptions and still return results

c#, task-parallel-library

Solution

I want to catch the exceptions, log the failures, but continue with the remaining tasks, even if they all fail.

In this case, the cleanest solution is to change what your code does for each element. I.e., this current code:

var downloads = urls.Select(url => httpClient.GetStringAsync(url));

says "for each url, download a string". What you want it to say is "for each url, download a string and then log and ignore any errors":

static async Task<string> DownloadAllAsync(IEnumerable<string> urls)
{
  var httpClient = new HttpClient();
  var downloads = urls.Select(url => TryDownloadAsync(httpClient, url));
  Task<string>[] downloadTasks = downloads.ToArray();
  string[] htmlPages = await Task.WhenAll(downloadTasks);
  return string.Concat(htmlPages);
}

static async Task<string> TryDownloadAsync(HttpClient client, string url)
{
  try
  {
    return await client.GetStringAsync(url);
  }
  catch (Exception ex)
  {
    Log(ex);
    return string.Empty; // or whatever you prefer
  }
}

Problem

I am updating my concurrency skillset. My problem seems to be fairly common: read from multiple Uris, parse and work with the result, etc. I have Concurrency in C# Cookbook. There are a few examples using GetStringAsync, such as ``` static async Task<string> DownloadAllAsync(IEnumerable<string> urls) { var httpClient = new HttpClient(); var downloads = urls.Select(url => httpClient.GetStringAsync(url)); Task<string>[] downloadTasks = downloads.ToArray(); string[] htmlPages = await Task.WhenAll(downloadTasks); return string.Concat(htmlPages); } ``` What I need is the asynchronous pattern for running multiple async tasks, capturing full or partial success. - Url 1 succeeds - Url 2 succeeds - Url 3 fails (timeout, bad Uri format, 401, etc) - Url 4 succeeds - ... 20 more with mixed success waiting on DownloadAllAsync task will throw a single aggregate exception if any fail, dropping the accumulated results. From my limited research, with WhenAll or WaitAll behave the same. I want to catch the exceptions, log the failures, but continue with the remaining tasks, even if they all fail. I could process them one by one, but wouldn't that defeat the purpose of allowing TPL to manage the whole process? Is there a link to a pattern which would accomplish this in a pure TPL way? Perhaps I'm using the wrong tool?

Original source