I'm working on a Web Hook in .NET 4.0 that will run a lambda asynchronously and then post the result to a given URI when it is finished.
I've got that to work, but now I want the Task to handle any exceptions that are thrown, and am finding it difficult to stop them from reaching the parent.
Here's part of my code:
private readonly Func<T> _startTask;
private readonly string _responseUri;
public Task<T> Begin()
{
var task = new Task<T>(_startTask);
task.ContinueWith<T>(End);
task.Start();
return task;
}
private T End(Task<T> task)
{
if (task.IsFaulted)
{
return HandleException(task);
}
var result = task.Result;
WebHookResponse.Respond(result, _responseUri);
return result;
}
private T HandleException(Task<T> task)
{
WebHookResponse.HandleException(task.Exception.InnerException, _responseUri);
return null;
}
An alternative version that I have tried calls ContinueWith()
twice to register one continuation to run OnlyOnRanToCompletion
and one to run OnlyOnFaulted
. (I'm not sure if calling ContinueWith()
twice is correct.):
public Task<T> Begin()
{
var task = new Task<T>(_startTask);
task.ContinueWith<T>(End, TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith<T>(HandleException, TaskContinuationOptions.OnlyOnFaulted);
task.Start();
return task;
}
private T End(Task<T> task)
{
var result = task.Result;
WebHookResponse.Respond(result, _responseUri);
return result;
}
private T HandleException(Task<T> task)
{
WebHookResponse.HandleException(task.Exception.InnerException, _responseUri);
return null;
}
So basically I want a way for each Task handle its own exceptions via a continuation function. As it stands the HandlException continuation function is never being called in either of the above examples.
I am causing the exceptions in a test case, and I should mention that I am using a Tasks.WaitAll(tasks);
call on an array of Tasks to make sure all of the tasks are complete before making my assertions, and I am not sure if that call makes a difference to how exceptions are handled by the Tasks. Currently WaitAll throws an AggregationException which aggregates the exceptions for each of the Tasks because they aren't being handled by the HandleException continuation function.
You can also handle the original exceptions by using the AggregateException. Handle method. Even if only one exception is thrown, it is still wrapped in an AggregateException exception, as the following example shows. public static partial class Program { public static void HandleThree() { var task = Task.
If Bar throws an exception, it will be thrown right at the point where you call it.
try { t. Start(); await t; } catch (Exception e) { // When awating on the task, the exception itself is thrown. // in this case a regular Exception. } } In TPL, When throwing an exception inside a Task, it's wrapped with an AggregateException.
A task continuation that observes the task's exception doesn't handle the exception. It still happens on wherever you wait on the task to finish.
You said you were calling WaitAll(tasks) before asserting. I'm betting that your continuation would have run if you gave it enough time, but the exception on WaitAll() is usually going to occur before your continuation runs. So your asserts probably failed before your continuation was given a chance to finish its work.
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