I have seen a similar question being asked here but it does not seem quite right for my scenario.
We have a UI that can execute a request and if the user wants to execute the request again (with a different query parameter) we want to abandon the initial request, ignore its response and only use the latest requests response.
At the moment I have:
private readonly IDataService _dataService;
private readonly MainViewModel _mainViewModel;
private CancellationTokenSource _cancellationTokenSource;
//Constructor omitted for brevity
public async void Execute()
{
if (_cancellationTokenSource != null)
{
_cancellationTokenSource.Cancel();
}
_cancellationTokenSource = new CancellationTokenSource();
try
{
string dataItem = await _dataService.GetDataAsync(_mainViewModel.Request, _cancellationTokenSource.Token);
_mainViewModel.Data.Add(dataItem);
}
catch (TaskCanceledException)
{
//Tidy up ** area of concern **
}
}
This seems to function well and I have a nice and responsive UI but I have a scenario that concerns me:
- A request is made by the user
- The user makes a new request which cancels the original request
- The new request returns before the original cancelled request throws its exception populating the UI with the currently required data
- The exception is thrown and clean up occurs overwriting the new requests output
This may be very rare but I can see it as a possibility unless my understanding of this is wrong.
Is there any way to ensure that if a Task is cancelled via a cancellation token request and a new Task is started the cancellation happens before the new task is started/returns execution without blocking the UI thread?
Any reading to expand my understanding on this would be most appreciated.
First, some related reading and questions, as requested:
If I understood your question correctly, your major concern is that the previous instance of the same task may update the UI (or
ViewModel
) with obsolete results, once it has completed.To make sure this does not happen, use
ThrowIfCancellationRequested
with the corresponding token before your're going to update the UI/model, everywhere you do that. Then it would not matter if the most recent instance of the task completes before the previous older one. The older task will reach theThrowIfCancellationRequested
point before it might have a chance to do anything harmful, so you don't have toawait
the older task:A different concern is what to do in the situation when the previous task fails with anything else than
TaskCanceledException
. This can too happen after the newer task has completed. It is up to you to decide whether to ignore this exception, re-throw it, or do anything else: