I have some code I'm downgrading from .NET 4.5's lovely async
and await
keywords to .NET 4.0. I'm using ContinueWith
to create a continuation similar to the way await
works.
Basically, my old code was:
var tokenSource = newCancellationTokenSource();
var myTask = Task.Run(() =>
{
return MyStaticClass.DoStuff(tokenSource.Token);
}, tokenSource.Token);
try
{
var result = await myTask;
DoStuffWith(result);
}
catch (OperationCanceledException)
{
// Cancel gracefully.
}
(As one might expect, MyStaticClass.DoStuff(token)
regularly calls token.ThrowIfCancellationRequested()
.)
My new code looks like this:
var tokenSource = new CancellationTokenSource();
try
{
Task.Factory.StartNew(() =>
{
return MyStaticClass.DoStuff(tokenSource.Token);
}, tokenSource.Token)
.ContinueWith(task =>
{
var param = new object[1];
param[0] = task.Result;
// I need to use Invoke here because "DoStuffWith()" does UI stuff.
Invoke(new MyDelegate(DoStuffWith, param));
});
}
catch (OperationCanceledException)
{
// Cancel gracefully.
}
However, the OperationCanceledException
is never caught. What's going on? Where do I put my try/catch block?
The wait handle of the cancellation token will become signaled in response to a cancellation request, and the method can use the return value of the WaitAny method to determine whether it was the cancellation token that signaled. The operation can then just exit, or throw an OperationCanceledException, as appropriate.
Don't throw OperationCanceledException after you've completed the work, just because the token was signaled. Return a successful result and let the caller decide what to do next.
The background long-running tasks are cancelled by using the CancellationToken object in Blazor.
Cancellation is handled differently from other exceptions. Basically, you can use this pattern:
Task.Factory.StartNew(() =>
{
// The task
}, tokenSource.Token)
.ContinueWith(task =>
{
// The normal stuff
}, TaskContinuationOptions.OnlyOnRanToCompletion)
.ContinueWith(task =>
{
// Handle cancellation
}, TaskContinuationOptions.OnlyOnCanceled)
.ContinueWith(task =>
{
// Handle other exceptions
}, TaskContinuationOptions.OnlyOnFaulted);
Or the alternative one:
Task.Factory.StartNew(() =>
{
// The task
}, tokenSource.Token)
.ContinueWith(task =>
{
switch (task.Status)
{
case TaskStatus.RanToCompletion:
// The normal stuff
break;
case TaskStatus.Canceled:
// Handle cancellation
break;
case TaskStatus.Faulted:
// Handle other exceptions
break;
}
});
In your case, you're not catching anything because:
Task.Factory.StartNew
returns immediately and always succeeds.task.Result
throws an AggregateException
since the task is canceledThe exception is not handled by anything since it's thrown from a thread pool thread. Oops. What happens next depends on the framework version:
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