I was under the impression that controlling flow with exceptions was considered a bad practice.
So then why would you do something like this:
var task = Task.Factory
.StartNew(() => command.Execute());
task.ContinueWith(t =>
{
// success callback
}, TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(t =>
{
Log.Error(string.Format("'{0}' failed.", command.GetType()), t.Exception);
// error callback
}, TaskContinuationOptions.OnlyOnFaulted);
When you could just as easily catch the exception inside command.Execute(), is there something i’m missing here? Can tasks throw exceptions unrelated to the code they’re executing?
EDIT:
How about if we’re using c# 5’s async and await keywords, would you say this would be better, or does catch all really not matter as in the above example?
public class AsyncFooCommand : AsyncCommandBase<Bar>
{
public override Bar Execute()
{
try
{
var bar = // Do something that can throw SpecificException
Successful = true;
return bar;
}
catch (SpecificException ex)
{
// Log
}
}
}
public static class AsyncCommandExecutor<T>
{
// NOTE: don't care about sharing this between instances.
// ReSharper disable StaticFieldInGenericType
private static readonly ILog Log = LogManager.GetLogger(typeof(Infrastructure.Commands.AsyncCommandExecutor<>));
// ReSharper restore StaticFieldInGenericType
public static async Task<T> Execute(IAsyncCommand<T> command, Action<T> success = null, Action error = null)
{
var task = Task.Factory
.StartNew(() =>
{
return command.Execute();
});
task.ContinueWith(t => Log.Error(string.Format("'{0}' failed, something terrible happened.", command.GetType()), t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
T result = await task;
if (success != null && command.Successful)
{
success(result);
}
if (error != null && !command.Successful)
{
error();
}
return result;
}
}
You certainly could and there would be no need for the continuation. It’s simply a different approach.
However, if you catch the exception in the task and it runs to completion, to the outside world the task looks like it was successful instead of failing. If you have other continuations that are
TaskContinuationOptions.OnlyOnRanToCompletionor other such options, and you technically failed to execute the command (and just caught the exception), that task will continue to run perhaps when it needed the antecedent to run successfully. It’s more about task state management.