I'm dealing with the situation where the task I start may throw, while still executing synchronously on the initial thread. Something like this, for illustrative purposes:
static async Task TestAsync()
{
var random = new Random(Environment.TickCount).Next();
if (random % 2 != 0)
throw new ApplicationException("1st");
await Task.Delay(2000);
Console.WriteLine("after await Task.Delay");
throw new ApplicationException("2nd");
}
From the calling code, I'd like to be able to catch any exceptions, possibly thrown from the synchronous part (i.e., until await Task.Delay()
). Here's how I'm currently doing it:
static void Main(string[] args)
{
try
{
var task = TestAsync();
if (task.IsFaulted)
task.GetAwaiter().GetResult();
Console.WriteLine("TestAsync continues asynchronously...");
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.ToString());
}
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
This works, although it looks a bit mouthful, as there is no Result
on the Task
.
I've also tried task.Wait()
instead of task.GetAwaiter().GetResult()
. That always gives me AggregateException
which I have to unwrap (rather than expected ApplicationException
directly).
Is there any other options?
[EDITED] To address the comments: I do this, because if the task fails instantly, I don't want to add it to the list of the pending tasks I maintain. The task itself knows nothing about such a list (and it doesn't have to). I still want to log the exception, and make user aware of it. I could also do throw task.Exception
, but that wouldn't give the exception stack frame captured with ExceptionDispatchInfo
.
[UPDATE] Inspired by other answers and comments: if I have full control over TestAsync
and I don't want introducing new class members, I also could do something like below. It might come handy when validating arguments:
static Task TestAsync(int delay)
{
if (delay < 0)
throw new ArgumentOutOfRangeException("delay");
Func<Task> asyncPart = async () =>
{
Console.WriteLine("await Task.Delay");
await Task.Delay(delay);
throw new ApplicationException("2nd");
};
return asyncPart();
}
I'd split it into two parts, rather than relying on task.GetAwaiter().GetResult()
to work. I'd be afraid that someone maintaining TestAsync
could unwittingly break things in the future.
This is how I would write it. This should preserve the behavior you've got, but I find it more obvious what's going on:
static Task Test()
{
var random = new Random(Environment.TickCount).Next();
if (random % 2 != 0)
throw new ApplicationException("1st");
return TestAsync();
}
static async Task TestAsync()
{
await Task.Delay(2000);
Console.WriteLine("after await Task.Delay");
throw new ApplicationException("2nd");
}
static void Main(string[] args)
{
try
{
Test();
Console.WriteLine("TestAsync continues asynchronously...");
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.ToString());
}
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
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