There are several scenarios where I need to invoke multiple asynchronous calls (from the same event handler) that can proceed independently of each other, with each one havi
The traditional approach was to use ContinueWith for registering the respective continuation to each asynchronous task:
private async void button_Click(object sender, RoutedEventArgs e)
{
TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
await Task.WhenAll(
GetNameAsync().ContinueWith(nameTask => { nameTextBox.Text = nameTask.Result; }, uiScheduler),
GetCityAsync().ContinueWith(cityTask => { cityTextBox.Text = cityTask.Result; }, uiScheduler),
GetRankAsync().ContinueWith(rankTask => { rankTextBox.Text = rankTask.Result; }, uiScheduler));
}
With C# 5, it is now preferable to use an await-style pattern. This may be most easily achieved by splitting up each task–continuation pair into its own method:
private async void button_Click(object sender, RoutedEventArgs e)
{
await Task.WhenAll(
PopulateNameAsync(),
PopulateCityAsync(),
PopulateRankAsync());
}
private async Task PopulateNameAsync()
{
nameTextBox.Text = await GetNameAsync();
}
private async Task PopulateCityAsync()
{
cityTextBox.Text = await GetCityAsync();
}
private async Task PopulateRankAsync()
{
rankTextBox.Text = await GetRankAsync();
}
Defining all these trivial methods quickly become cumbersome, so one may condense them into async lambdas instead:
private async void button_Click(object sender, RoutedEventArgs e)
{
await Task.WhenAll(
new Func(async () => { nameTextBox.Text = await GetNameAsync(); })(),
new Func(async () => { cityTextBox.Text = await GetCityAsync(); })(),
new Func(async () => { rankTextBox.Text = await GetRankAsync(); })());
}
If this pattern is used frequently, it would also be helpful to define a utility method that can take the Func
lambdas and execute them, making our event handler's code more concise and readable:
public static Task WhenAllTasks(params Func[] taskProducers)
{
return Task.WhenAll(taskProducers.Select(taskProducer => taskProducer()));
}
private async void button_Click(object sender, RoutedEventArgs e)
{
await WhenAllTasks(
async () => nameTextBox.Text = await GetNameAsync(),
async () => cityTextBox.Text = await GetCityAsync(),
async () => rankTextBox.Text = await GetRankAsync());
}