Reputation: 5133
private async void TriggerWeekChanged(Week currentWeek)
{
await LoadDataForSelectedWeek(currentWeek); //Split into multiple methods
}
In case a user hammers on the Change_Week
Button how can I cancel the current Task, and start a new one with the new paramerters ?
I tried like this:
private async Task Refresh(Week selectedWeek, CancellationToken token)
{
Collection.Clear();
await LoadDataFromDatabase();
token.ThrowIfCancellationRequested();
await ApplyDataToBindings();
token.ThrowIfCancellationRequested();
//Do some other stuff
}
Problem is:
In my Collection
I got data from multiple weeks when I hit the button to fast in a row.
Upvotes: 3
Views: 1650
Reputation: 533
You did not mention about how LoadDataForSelectedWeek
and Refresh
interact.
For short you need to create one CancellationTokenSource
instance that handle every click. Then pass it to that method and do Cancel
method every time new one appear.
private async void TriggerWeekChanged(Week currentWeek, CancellationTokenSource tokenSource)
{
tokenSource.Cancel();
try
{
var loadDataTask = Task.Run(() => LoadDataForSelectedWeek(currentWeek, tokenSource.Token), tokenSource.Token); //Split into multiple methods
}
catch(OperationCanceledException ex)
{
//Cancelled
}
}
LoadDataForSelectedWeek -> Refresh (?)
private async Task Refresh(Week selectedWeek, CancellationToken token)
{
Collection.Clear();
await LoadDataFromDatabase();
token.ThrowIfCancellationRequested();
await ApplyDataToBindings();
token.ThrowIfCancellationRequested();
//Do some other stuff
}
Upvotes: 3
Reputation: 17001
Everything that is awaited will need to have visibility on that CancellationToken
. If it is a custom Task
that you wrote, it should accept it as an argument, and periodically within the function, check to see if it has been cancelled. If so, it should take any actions needed (if any) to stop or rollback the operation in progress, then call ThrowIfCancellationRequested()
.
In your example code, you propbably want to pass token
in to LoadDataFromDatabase
and ApplyDataToBindings
, as well as any children of those tasks.
There may be some more advanced situations where you don't want to pass the same CancellationToken
in to child tasks, but you still want them to be cancellable. In those cases, you should create a new, internal to the Task
, Cancellationtoken
that you use for child tasks.
An important thing to remember is that ThrowIfCancellationRequested
marks safe places within the Task
that the Task
can be stopped. There is no guaranteed safe way for the runtime to automatically detect safe places. If the Task
were to automatically cancel its self as soon as cancellation was requested, it could potentially be left in an unknown state, so it is up to developers to mark those safe locations. It isn't uncommon to have several calls to check cancellation scattered throughout your Task
.
I've just notice that your TriggerWeekChanged
function is async void
. This is usually considered an anti-pattern when it is used on something that is not an event handler. It can cause a lot of problems with tracking the completed status of the async
operations within the method, and handling any exceptions that might be thrown from within it. You should be very weary of anything that is marked as async void
that isn't an event handler, as it is the wrong thing to do 99%, or more, of the time. I would strongly recommend changing that to async Task
, and consider passing in the CancellationToken
from your other code.
Upvotes: 3