Xamarin PCL - 根据用户输入进行 API 调用,如何抑制请求?

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

我有一个搜索任务,在便携式 class 库中向 API 发出请求,当用户在文本框中输入文本时,这按预期工作,但我有一个问题规模上的过度表现。当我们有一个庞大的用户群时,在每次按键时都向这个 API 发出请求,我可以预见到性能问题。

我已将 API 调用限制为仅在超过三个有效字符时触发,但我想进一步抑制这种情况。我可以在此基础上实现一个计时器,但感觉它不是一个好的解决方案,并且不存在于 PCL 框架中。

是否有推荐的模式来实现此类请求抑制?

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

通常这是用定时器来完成的。当搜索文本更改时,您启动(或重复使用)一个计时器,该计时器将在延迟后触发并执行搜索请求。如果在该延迟期间键入了更多文本 - 计时器将重置。示例代码:

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);
    }
}

如果定时器不可用,你可以用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);
    }
}

Cheap/Fast 实现方法是 Task.Delay:

var mySearchThread = new Thread (new ThreadStart (async delegate {
   while (true) {
       if (!String.IsNullOrWhiteSpace(seachText) {
          YourSearchMethod(seachText)
       };

       InvokeOnMainThread ( () => {
          // Refresh your datasource on the UIthread
       });

       await Task.Delay (2000);
   }
})).Start ();

基于 PCL 的解决方案(以及使用 出色框架 的惊人干净方式)是使用 ReactiveUI 节流 (Throttle),那么你可以做这样的壮举:

// Throttle searching to every 2 seconds
this.WhenAnyValue(x => x.SearchText)
    .Where(x => !String.IsNullOrWhiteSpace(x))
    .Throttle(TimeSpan.FromSeconds(2))
    .InvokeCommand(SearchCommand)

参考:http://reactiveui.net

参考:http://docs.reactiveui.net/en/user-guide/when-any/index.html