27

Today I was wondering how to transform a list of Tasks by awaiting each of it. Consider the following example:

private static void Main(string[] args)
{
    try
    {
        Run(args);                
        Console.ReadLine();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
        Console.ReadLine();
    }
}

static async Task Run(string[] args)
{
    //Version 1: does compile, but ugly and List<T> overhead
    var tasks1 = GetTasks();                       

    List<string> gainStrings1 = new List<string>();
    foreach (Task<string> task in tasks1)
    {
        gainStrings1.Add(await task);
    }
    Console.WriteLine(string.Join("", gainStrings1));

    //Version 2: does not compile
    var tasks2 = GetTasks();
    IEnumerable<string> gainStrings2 = tasks2.Select(async t => await t);
    Console.WriteLine(string.Join("", gainStrings2));
}

static IEnumerable<Task<string>> GetTasks()
{
    string[] messages = new[] { "Hello", " ", "async", " ", "World" };

    for (int i = 0; i < messages.Length; i++)
    {
        TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
        tcs.SetResult(messages[i]);
        yield return tcs.Task;
    }
}

I'd like to transform my list of Tasks without the foreach, however either the anonymous function syntax nor the usual function syntax allows me to do what my foreach does.

Do I have to rely on my foreach and the List<T> or is there any way to get it to work with IEnumerable<T> and all its advantages?

GameScripting
  • 16,092
  • 13
  • 59
  • 98

1 Answers1

55

What about this:

await Task.WhenAll(tasks1);
var gainStrings = tasks1.Select(t => t.Result).ToList();

Wait for all tasks to end and then extract results. This is ideal if you don't care in which order they are finished.

EDIT2: Even better way:

var gainStrings = await Task.WhenAll(tasks1);
Toni Petrina
  • 7,014
  • 1
  • 25
  • 34
  • 3
    Rather than the `Select` you can just use the return value of `WhenAll`, it will be a `string[]` of all of the results of each task. – Servy May 02 '13 at 14:30
  • According to this: http://msdn.microsoft.com/en-us/library/hh194874.aspx it will do that only if you give it an array. – Toni Petrina May 02 '13 at 14:33
  • 3
    You don't need the `ToArray()`, `Task.WhenAll()` works for `IEnumerable>` too. – svick May 02 '13 at 14:35
  • 5
    [Nope](http://msdn.microsoft.com/en-us/library/hh194766.aspx). It returns an array of the results even if you pass in an `IEnumerable>` – Servy May 02 '13 at 14:35
  • EDIT2 is better because sometimes Visual Studio gives warnings about calling .Result on a task. – eyelesscactus54 Jun 25 '21 at 20:21