I am trying to wrap my head around async
/await
and wanted to know if this is the proper use of the Task.WhenAll
method:
public class AsyncLib
{
public async Task<IEnumerable<string>> DoIt()
{
var urls = new string[] { "http://www.msn.com", "http://www.google.com" };
var tasks = urls.Select(x => this.GetUrlContents(x));
var results = await Task.WhenAll(tasks);
return results.Select(x => x);
}
public async Task<string> GetUrlContents(string url)
{
using (var client = new WebClient())
{
return await client.DownloadStringTaskAsync(url);
}
}
}
Main
This is the calling console application.
class Program
{
static void Main(string[] args)
{
var lib = new AsyncLib();
foreach(var item in lib.DoIt().Result)
{
Console.WriteLine(item.Length);
}
Console.Read();
}
}
WhenAll(IEnumerable<Task>)Creates a task that will complete when all of the Task objects in an enumerable collection have completed.
The Task. WaitAll blocks the current thread until all other tasks have completed execution. The Task. WhenAll method is used to create a task that will complete if and only if all the other tasks have completed.
WhenAll returns control after all tasks are completed, while WhenAny returns control as soon as a single task is completed.
The big difference between them is that WaitAll return a Void (it blocks the current thread) instead, WhenAll, return a Task (we can decide to block or not the current thread).
The problem with your current code is that you won't be able to handle individual exceptions, if more than one task throws.
If this is a concern, then with the following approach, you can handle them:
public async Task<Task<string>[]> DoIt()
{
var urls = new string[] { "http://www.msn.com", "http://www.google.com" };
var tasks = urls.Select(x => this.GetUrlContents(x)).ToArray();
await Task.WhenAll(tasks);
return tasks;
}
// ...
static void Main(string[] args)
{
var lib = new AsyncLib();
foreach(var item in lib.DoIt().Result)
{
Console.WriteLine(item.Result.Length);
}
Console.Read();
}
Note I use ToArray()
to avoid evaluating the enumerable and starting the tasks for more than once (as LINQ is lazy-evaluated).
Updated, now you can further optimize DoIt
by eliminating async/await
:
public Task<Task<string>[]> DoIt()
{
var urls = new string[] { "http://www.msn.com", "http://www.google.com" };
var tasks = urls.Select(x => this.GetUrlContents(x)).ToArray();
return Task.Factory.ContinueWhenAll(
tasks,
_ => tasks,
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}
However, if you do so, be aware of the change in the exception propagation behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With