I've implemented a cancellable search in blazor. To illustrate:
When the user types multiple times, the previous search will be cancelled. This is achieved by using a cancellation token.
private bool _isLoading = false;
private CancellationTokenSource _cancellationTokenSrc;
public Func<string, CancellationToken, Task<IEnumerable<T>>> SearchFuncWithCancel { get; set; }
private void CancelToken()
{
try
{
_cancellationTokenSrc?.Cancel();
}
catch
{ }
_cancellationTokenSrc = new CancellationTokenSource();
}
private async Task OnSearchAsync()
{
IEnumerable<T> searched_items = Array.Empty<T>();
CancelToken();
try
{
_isLoading = true;
searched_items = (await SearchFuncWithCancel(Text, _cancellationTokenSrc.Token)) ?? Array.Empty<T>();
}
catch (Exception e)
{
Console.WriteLine("The search function failed to return results: " + e.Message);
}
finally
{
_isLoading = false;
}
//...
}
I am experiencing an issue with the code, where _isLoading
is always false for a subsequent search. You can see it in the gif. The loading indicator is displayed the very first time. After that it does not show up anymore.
When a search gets cancelled due to another search the SearchFuncWithCancel method will exit and the finally block will be called. This sets _isLoading to false, while the other search is still awaiting results.
Any ideas how to fix that? My first idea was to use a stack. Whenever search is invoked, we push a true onto the stack. In the finally block we pop from the stack. As long as there is something on the stack, _isLoading must be true. Not sure if that is the best solution for this problem.
It should be perfectly possible for something like this to happen:
The last step would set _isLoading
to false, even if the search for "b" is still in progress.
There are multiple potential ways to solve this:
_isLoading
with a property that checks the status of the search task. I.e. something like:var currentTask = SearchFuncWithCancel(...);
currentTaskField = currentTask;
searched_items = await currentTask;
...
public bool IsLoading => !currentTaskField.IsCompleted;
But I'm not sure what the best solution is for this particular case.