I will try to simplify my situation here to be more clean and concise. So, I am working on a WinRT application where user enters text in a TextBox
and in its TextChanged
event after 2 seconds have elapsed I need to make a remote request to fetch data based on user text.
Now user enters text and a web request has been initialized but immediately user writes another term. So, I need to cancel the first web request and fire the new one.
Consider the following as my code :
private CancellationTokenSource cts;
public HomePageViewModel()
{
cts = new CancellationTokenSource();
}
private async void SearchPeopleTextChangedHandler(SearchPeopleTextChangedEventArgs e)
{
//Cancel previous request before making new one
//GetMembers() is using simple HttpClient to PostAsync() and get response
var members = await _myService.GetMembers(someId, cts.Token);
//other stuff based on members
}
I know CancellationToken
plays a role here but I just cannot figure out how.
You've already almost got it. The core idea is that a single CancellationTokenSource
can only be canceled once, so a new one has to be created for each operation.
private CancellationTokenSource cts;
private async void SearchPeopleTextChangedHandler(SearchPeopleTextChangedEventArgs e)
{
// If there's a previous request, cancel it.
if (cts != null)
cts.Cancel();
// Create a CTS for this request.
cts = new CancellationTokenSource();
try
{
var members = await _myService.GetMembers(someId, cts.Token);
//other stuff based on members
}
catch (OperationCanceledException)
{
// This happens if this operation was cancelled.
}
}
I would implement the GetMembers
method like this:
private async Task<List<Member>> GetMembers(int id, CancellationToken token)
{
try
{
token.ThrowIfCancellationRequested();
HttpResponseMessage response = null;
using (HttpClient client = new HttpClient())
{
response = await client.PostAsync(new Uri("http://apiendpoint"), content)
.AsTask(token);
}
token.ThrowIfCancellationRequested();
// Parse response and return result
}
catch (OperationCanceledException ocex)
{
return null;
}
}
The rest is just calling the cts.Cancel()
method and creating a new instance of CancellationTokenSource
before calling GetMembers
each time in the handler. Of course, as @Russell Hickey mentioned, cts
should be global. (and even static if there are multiple instances of this class and you always want to cancel the GetMembers
method when this handler is invoked. Usually I also have a class which wraps the result and has an additional property IsSuccessful
to distinguish a real null
result from a failed operation.
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