and the xaml
<Page
x:Class="pc.asyncException.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:pc.asyncException"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1" Text="Async Exception Example"
Style="{ThemeResource HeaderTextBlockStyle}"
VerticalAlignment="Center" />
<StackPanel Grid.Column="1" Grid.Row="1">
<StackPanel Orientation="Horizontal">
<TextBox x:Name="Numerator_TXT"
Header="Numerator"
Text="0"/>
<TextBlock Text="/"
VerticalAlignment="Bottom"
FontSize="42" Margin="10 0" />
<TextBox x:Name="Denominator_TXT"
Header="Denominator"
Text="0"/>
<TextBlock Text="="
VerticalAlignment="Bottom"
FontSize="42" Margin="10 0" />
<TextBox x:Name="Result_TXT" Header="Result"
Text="0"/>
</StackPanel>
<Button x:Name="Synchronous_BTN"
Content="Synchronous"/>
<Button x:Name="Asynchronous_BTN" Content="Asynchronous"/>
<Button x:Name="Task1_BTN" Content="Task 1"/>
<Button x:Name="Task2_BTN" Content="Task 2"/>
<Button x:Name="Void_BTN" Content="Void"/>
<Button x:Name="AsyncTask_BTN"
Content="Async Task"/>
</StackPanel>
</Grid>
</Page>
using System;
using System.Threading.Tasks;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace pc.asyncException
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.Synchronous_BTN.Click += Synchronous_BTN_Click;
this.Asynchronous_BTN.Click += Asynchronous_BTN_Click;
this.Task1_BTN.Click += Task_BTN_Click;
this.Task2_BTN.Click += Task2_BTN_Click;
this.Void_BTN.Click += Void_BTN_Click;
this.AsyncTask_BTN.Click += AsyncTask_BTN_Click;
}
void Synchronous_BTN_Click(object sender, RoutedEventArgs e)
{
var n = Convert.ToInt32(this.Numerator_TXT.Text);
var d = Convert.ToInt32(this.Denominator_TXT.Text);
try
{
this.Result_TXT.Text = (n / d).ToString();
}
catch (DivideByZeroException ex)
{
this.Result_TXT.Text = "Sync
undefined";
}
}
async void Asynchronous_BTN_Click(object sender, RoutedEventArgs e)
{
var n = Convert.ToInt32(this.Numerator_TXT.Text);
var d = Convert.ToInt32(this.Denominator_TXT.Text);
try
{
this.Result_TXT.Text = await Task.Run(() => (n / d).ToString());
}
catch (DivideByZeroException ex)
{
this.Result_TXT.Text = "Async
undefined";
}
}
void Task_BTN_Click(object sender, RoutedEventArgs e)
{
var n = Convert.ToInt32(this.Numerator_TXT.Text);
var d = Convert.ToInt32(this.Denominator_TXT.Text);
try
{
//Silently fails
Task.Factory.StartNew(async () => await this.Dispatcher
.RunAsync(CoreDispatcherPriority.Normal,
() =>
this.Result_TXT.Text = (n /
d).ToString()));
}
catch (DivideByZeroException ex)
{
this.Result_TXT.Text = "Task
1 undefined";
}
}
void Task2_BTN_Click(object sender, RoutedEventArgs e)
{
var n = Convert.ToInt32(this.Numerator_TXT.Text);
var d = Convert.ToInt32(this.Denominator_TXT.Text);
try
{
this.Result_TXT.Text = Task.Factory.StartNew(() => (n / d).ToString()).Result;
}
catch (DivideByZeroException ex)
{
this.Result_TXT.Text = "Task
2 undefined";
}
catch (AggregateException ex)
{
this.Result_TXT.Text = "aggregate
undefined";
}
}
#region
Call Async Method that return void
void Void_BTN_Click(object sender, RoutedEventArgs e)
{
var n = Convert.ToInt32(this.Numerator_TXT.Text);
var d = Convert.ToInt32(this.Denominator_TXT.Text);
try
{
//Cant
await
CalculateAsync1(n, d);
}
catch (DivideByZeroException ex)
{
this.Result_TXT.Text = "Void
undefined";
}
}
async void CalculateAsync1(int n, int d)
{
this.Result_TXT.Text = (n / d).ToString();
}
#endregion
#region
Call Async Function that returns Task
async void AsyncTask_BTN_Click(object Sender, RoutedEventArgs e)
{
var n = Convert.ToInt32(this.Numerator_TXT.Text);
var d = Convert.ToInt32(this.Denominator_TXT.Text);
try
{
//Can await
await CalculateAsync2(n, d);
}
catch (DivideByZeroException ex)
{
this.Result_TXT.Text = "Async
Task undefined";
}
}
async Task CalculateAsync2(int n, int d)
{
this.Result_TXT.Text = await Task.Factory.StartNew(() => (n /
d).ToString());
}
#endregion
}
}
void Synchronous_BTN_Click(object sender, RoutedEventArgs e)
{
var n = Convert.ToInt32(this.Numerator_TXT.Text);
var d = Convert.ToInt32(this.Denominator_TXT.Text);
try
{
this.Result_TXT.Text = (n / d).ToString();
}
catch (DivideByZeroException ex)
{
this.Result_TXT.Text = "Sync
undefined";
}
}
Next let's take a look at utilizing the async and await keywords.
async void Asynchronous_BTN_Click(object sender, RoutedEventArgs e)
{
var n = Convert.ToInt32(this.Numerator_TXT.Text);
var d = Convert.ToInt32(this.Denominator_TXT.Text);
try
{
this.Result_TXT.Text = await Task.Run(() => (n / d).ToString());
}
catch (DivideByZeroException ex)
{
this.Result_TXT.Text = "Async
undefined";
}
}
Thirdly lets look at the isolated task method, that is everything is done in a separate task which updates the UI via the dispatcher, this allows us to update the UI Thread
void Task_BTN_Click(object sender, RoutedEventArgs e)
{
var n = Convert.ToInt32(this.Numerator_TXT.Text);
var d = Convert.ToInt32(this.Denominator_TXT.Text);
try
{
Task.Factory.StartNew(async () => await this.Dispatcher
.RunAsync(CoreDispatcherPriority.Normal,
() =>
this.Result_TXT.Text = (n / d).ToString()));
}
catch (DivideByZeroException ex)
{
this.Result_TXT.Text = "Task
1 undefined";
}
}
and now lets look at a better way to utilize the TPL
void Task2_BTN_Click(object sender, RoutedEventArgs e)
{
var n = Convert.ToInt32(this.Numerator_TXT.Text);
var d = Convert.ToInt32(this.Denominator_TXT.Text);
try
{
this.Result_TXT.Text = Task.Factory.StartNew(() => (n / d).ToString()).Result;
}
catch (DivideByZeroException ex)
{
this.Result_TXT.Text = "Task
2 undefined";
}
catch(AggregateException ex)
{
this.Result_TXT.Text = "aggregate
undefined";
}
}
Now we could potentially end up with various nested InnerExceptions, luckily there's a flatten function
void Task2_BTN_Click(object sender, RoutedEventArgs e)
{
var n = Convert.ToInt32(this.Numerator_TXT.Text);
var d = Convert.ToInt32(this.Denominator_TXT.Text);
try
{
this.Result_TXT.Text = Task.Factory.StartNew(() => (n / d).ToString()).Result;
}
catch (DivideByZeroException ex)
{
this.Result_TXT.Text = "Task
2 undefined";
}
catch (AggregateException ex)
{
this.Result_TXT.Text = "aggregate
undefined";
foreach(var Ex in ex.Flatten().InnerExceptions)
{
//log
the exceptions
}
}
}
#region Call Async Method that return void
void Void_BTN_Click(object sender, RoutedEventArgs e)
{
var n = Convert.ToInt32(this.Numerator_TXT.Text);
var d = Convert.ToInt32(this.Denominator_TXT.Text);
try
{
//Cant
await
CalculateAsync1(n, d);
}
catch (DivideByZeroException ex)
{
this.Result_TXT.Text = "Void
undefined";
}
}
async void CalculateAsync1(int n, int d)
{
this.Result_TXT.Text = (n / d).ToString();
}
#endregion
now let's look at a asynchronous function that instead returns a task
#region Call Async Function that returns Task
async void AsyncTask_BTN_Click(object Sender, RoutedEventArgs e)
{
var n = Convert.ToInt32(this.Numerator_TXT.Text);
var d = Convert.ToInt32(this.Denominator_TXT.Text);
try
{
//Can await
await CalculateAsync2(n, d);
}
catch (DivideByZeroException ex)
{
this.Result_TXT.Text = "Async
Task undefined";
}
}
async Task CalculateAsync2(int n, int d)
{
this.Result_TXT.Text = await Task.Factory.StartNew(() => (n /
d).ToString());
}
#endregion
one thing to note is that when you fire a task without using the await keyword, or without somehow waiting for the result and an exception occurs inside that task, it happens inside an anonymous task and thus is not observed hence the term unobserved exception, this can be a real drag because let's say that you have an anonymous task that saves data to a web service well your application has no way of knowing that something went wrong potentially creating a poor user experience. Imagine saving thinking everything went great, only to find out next time your run your app that you lost all your work.