我正在尝试一个"type-to-search"的react - extensions示例,它从文本框(WPF应用程序,如果它重要的话)中获取一个字符串,并执行一个可能很长的服务器端搜索(在我的情况下用Thread.Sleep模拟),并在列表框中显示结果。
这个特定"模块"的关键特性是:
- 异步搜索;UI在搜索 时不冻结
- 控制它;快速键入不会为每次击键生成服务器端搜索
- 不同的搜索;在搜索"asd"并显示结果后,快速键入[退格],d(即删除最后一个字符并快速重新键入)将不会重做服务器端搜索
- drop-intermediary-results;如果我输入"asd"并短暂等待(导致服务器端搜索启动),然后,在asd的结果显示之前完成搜索字符串,这些中间/特定结果被删除
问题是,在heavy方法(做'服务器端搜索'的方法)的单个异常之后,订阅被终止并且不能使用
到目前为止,我只通过重新订阅IObservable对象找到了一个解决方案,但这感觉不对。我也尝试过。retry(),但是虽然我可以重用订阅,但我的OnError
处理程序不再被调用。
代码如下:
private IObservable<object> _resultsFromTypeToSearch;
private void SetupObserver()
{
var throttledUserInput =
(from evt in Observable.FromEventPattern<TextChangedEventArgs>(TxtSearch, "TextChanged")
select ((TextBox)evt.Sender).Text)
.Throttle(TimeSpan.FromSeconds(0.6)) // this ensures that only after 0.6 seconds the user input is taken into consideration
.DistinctUntilChanged(); // this ensures only changed input is taken into consideration
var getDataFunc = new Func<string, object>(GetExpensiveData);
var searchAsync = Observable.FromAsyncPattern<string, object>(getDataFunc.BeginInvoke, getDataFunc.EndInvoke);
var z = from text in throttledUserInput
from word in searchAsync(text).TakeUntil(throttledUserInput)
select word; // TakeUntil will drop an ongoing search if a new search is requested in the meantime
_resultsFromTypeToSearch = z.ObserveOn(TxtSearch); // this ensures that we'll get notified on the UI thread
_resultsFromTypeToSearch.Subscribe(PresentResults, OnError);
}
private void OnError(Exception obj)
{
ClearUI();
MessageBox.Show("Error");
_resultsFromTypeToSearch.Subscribe(PresentResults, OnError); // THIS IS MY WORKAROUND WHICH FEELS BAD
}
private void ClearUI()
{
IsBusy = false;
Results.Clear();
}
private void PresentResults(object result)
{
ClearUI();
Results.Add(result.ToString());
}
private object GetExpensiveData(string searchString)
{
IsBusy = true;
if (DateTime.Now.Millisecond % 3 == 0) throw new ServerException();
Thread.Sleep(2000);
return "Data for " + searchString;
}
有更好的方法吗?
Catch
运算符在这里有帮助。
在你的代码中,我认为你可以这样做,以防止订阅结束时,searchAsync失败:
var searchAsync = Observable
.FromAsyncPattern<string, object>(getDataFunc.BeginInvoke, getDataFunc.EndInvoke)
.Catch(e => Observable.Empty<object>()) // eat the error and return "no results" for this search.
你可以返回任何你想要的新observable。您可以记录错误并返回另一个搜索尝试。在本例中,我只是返回一个空的可观察对象,这使得整个订阅行为就好像搜索尝试从未发生过。
我误解了Rx。一个序列一旦出现故障就不能被重用。简单的解决方案是重新创建订阅,即:再次调用SetupObserver()