dimanche 29 décembre 2019

How to properly cancel an asynchronous and running method if it is invoked again and the previous invokation result is obsolete

I have a rather generic question but with a specific use case.

In my specific case I have a UI with an input text box and if the user is changing the text in that input box some recommendations are shown to what the user might want to type in eventually. In other words the input box uses an autocomplete feature. The recommendations are requested from a webservice, hence this process takes a significant amount of time in which the user might already changed the text again. Hence, I want to cancel or stop the process that is still acquiring obsolete data and only take the results of the most recent invokation of the method.

So imagine a method OnTextChanged in the View class that is calling the method GetRecommendationsAsync. My approach is to simply cancel the previous and obsolete invokation of GetRecommendationsAsync and then don't do anything in the UI if the method is canceled. Here some minimal code I just wrote up (so could contain errors) to show the concept:

public async Task OnTextChangedAsync(string newText)
{
    try
    {
        var recommendations = await GetLatestRecommendationsAsync(newText);
        ShowRecommendations(recommendations);
    }
    catch(TaskCanceledException)
    {
    }
}

private Task<Recommendations[]> GetLatestRecommendationsAsync(string text)
{
     _cancellationTokenSource?.Cancel();
     _cancellationTokenSource = new CancellationTokenSource();

     if (string.IsNullOrWhiteSpace(text)) return null;

     return GetRecommendationsAsync(text, _cancellationTokenSource.Token);
}

Now, my question is if this is a valid approach or does it have some shortcommings? Furthermore, I would like to know if there is a general known pattern to handle the generic case of cancelling a method that is obsolete because it was called again? Is an appraoch using a semaphore better? How would you approach this case? Is it guaranteed that the cancellation of the previous invokation is caught before the new invocation returns its results?

Aucun commentaire:

Enregistrer un commentaire