2016-05-06 20 views
1

当用户在文本框中输入文本时,我有一个搜索任务向便携式类库中的API发出请求,这可以按预期工作,但我担心大小会影响性能。当我们有一个庞大的用户群,每个按键都向这个API发出请求时,我可以预见性能问题。Xamarin PCL - 根据用户输入进行API调用,如何抑制请求?

我已经限制了API调用只有在有三个以上有效字符时才会触发,但我想进一步抑制这一点。我可以在上面实现一个定时器,但它不像一个好的解决方案,并且不存在于PCL框架中。

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

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

为什么定时器感觉并不好? – Evk

+0

@Evk一个计时器的实现对我来说有点不好意思。这是一个有趣的话题,因为我需要这个工作在一个不包含定时器实现的可移植类库中。我会更新我的问题以反映这一点。 –

+0

并且Task.Delay至少可用? – Evk

回答

2

通常这是用定时器完成的。当搜索文本改变时,你开始(或重新使用)一个定时器,它将在延迟后触发并执行搜索请求。如果在延迟期间输入更多文本 - 定时器将被重置。示例代码:

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

便宜/快速的方式是一个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

+0

这不仅仅是每2秒执行一次服务请求吗? – Evk

+0

@Evk是的,根据您选择何时退出'while(true)'(即,只要SearchBar存在)每隔2秒过滤一次数据源,如果搜索文本已更改并更新Ui ... cheap /快......但我更喜欢在'ReactiveUI'中使用'命令'限制并将其绑定到我的搜索中ViewModel – SushiHangover

+0

即使没有命令和内容,也有一种更好的方式来使用Task.Delay,而不是经常用无用的请求轮询服务,每2秒。如果我离开午餐并离开输入领域,该怎么办? – Evk