Search code examples
c#.netsearchxamarinportable-class-library

Xamarin PCL - Making API calls based on user entry, how to dampen requests?


I have a search task that makes a request to an API, within a portable class library, when the user enters text in the textbox, this works as expected but I have a concern over performance at scale. When we have a large userbase all making requests to this API on every key press I can foresee performance issues.

I have limited the API call to only fire when there are more than three valid characters but I want to dampen this further. I could implement a timer over the top of this but it does not feel like a good solution and is not present in the PCL framework.

Is there a recommended pattern to achieve this type of request dampening?

    private async Task GetClubs()
    {
        try
        {
            if (!string.IsNullOrWhiteSpace(ClubSearch) && ClubSearch.Replace(" ", "").Length >= 3)
            {
                Clubs = await dataService.GetClubs(ClubSearch);
            }
        }
        catch (DataServiceException ex)
        {
            ...
        }
    }

Solution

  • Usually that is done with timer. When search text changes you start (or reuse) a timer which will fire after delay and execute search request. If more text is typed during that delay - timer is reset. Sample code:

    public class MyClass {
        private readonly Timer _timer;
        const int ThrottlePeriod = 500; // ms
        public MyClass() {
            _timer = new System.Threading.Timer(_ => {
                ExecuteRequest();
            }, null, Timeout.Infinite, Timeout.Infinite);
        }
    
        private string _searchTerm;
        public string SearchTerm
        {
            get { return _searchTerm; }
            set
            {
                _searchTerm = value;
                ResetTimer();
            }
        }
    
        private void ResetTimer() {
            _timer.Change(ThrottlePeriod, Timeout.Infinite);
        }
    
        private void ExecuteRequest() {
            Console.WriteLine(SearchTerm);
        }
    }
    

    If timer is not available, you can do the same with Task.Delay:

    public class MyClass
    {        
        const int ThrottlePeriod = 500; // ms
        private string _searchTerm;
        public string SearchTerm
        {
            get { return _searchTerm; }
            set
            {
                _searchTerm = value;
                SearchWithDelay();              
            }
        }
    
        private async void SearchWithDelay() {
            var before = this.SearchTerm;
            await Task.Delay(ThrottlePeriod);
            if (before == this.SearchTerm) {
                // did not change while we were waiting
                ExecuteRequest();
            }
        }        
    
        private void ExecuteRequest()
        {
            Console.WriteLine(SearchTerm);
        }
    }