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
private async void button_Click(object sender, RoutedEventArgs)
{
var nameTask = GetNameAsync();
var cityTask= GetCityAsync();
var rankTask= GetRankAsync();
System.Threading.Tasks.Task.WaitAll(nameTask, cityTask, rankTask);
nameTextBox.Text = nameTask.Result;
cityTextBox.Text = cityTask.Result;
rankTextBox.Text = rankTask.Result;
}
More details: https://msdn.microsoft.com/pt-br/library/dd270695(v=vs.110).aspx
A simpler alternative would be:
private async void button_Click(object sender, RoutedEventArgs e)
{
var results = await Task.WhenAll(
GetNameAsync(),
GetCityAsync(),
GetRankAsync()
);
nameTextBox.Text = results[0];
nameCityBox.Text = results[1];
nameRankBox.Text = results[2];
}
No closures, no extra state machines.
As I understand it, you need to query three async resources and only update the UI once all three have returned. If that's correct the Task.WhenAll control is idea to use. Something like
private async void button_Click(object sender, RoutedEventArgs e)
{
string nametext = null;
string citytext = null;
string ranktext = null;
await Task.WhenAll(
async () => nametext = await GetNameAsync(),
async () => citytext = await GetCityAsync(),
async () => ranktext = await GetRankAsync()
);
nameTextBox.Text = nametext;
nameCityBox.Text = citytext;
nameRankBox.Text = ranktext;
}
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<Task>(async () => { nameTextBox.Text = await GetNameAsync(); })(),
new Func<Task>(async () => { cityTextBox.Text = await GetCityAsync(); })(),
new Func<Task>(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<Task>
lambdas and execute them, making our event handler's code more concise and readable:
public static Task WhenAllTasks(params Func<Task>[] 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());
}