我正在编写一个用户界面,用户可以在其中输入搜索字词,列表会不断更新,提供建议。
我的第一个原因是Rx原始Throttle是一个完美的匹配,但它让我在那里一半。
建议需要一段时间才能获取,所以我不是在UI线程上异步获取它们。
问题是如果用户再次输入节流时间范围,我想丢弃/跳过/丢弃结果。
例如:
现在有了Throttle,第一次获取仍将继续填充gui建议列表。 我想学习的是如何取消首次获取,因为它不再相关? 只有第二个按键才能触发对gui的更新。
这是我试过的
(我使用ReactiveUI,但Q是关于Rx)
public IEnumerable<usp_getOpdrachtgevers_Result> Results { get; set; } // [Reactive] pu
public SearchOpdrachtgeverVM()
{
this.WhenAnyValue(x => x.FirstName,
x => x.LastName
)
.Throttle(TimeSpan.FromMilliseconds(200))
.Subscribe(async vm => Results = await PopulateGrid());
}
private async Task<IEnumerable<usp_getOpdrachtgevers_Result>> PopulateGrid()
{
return await Task.Run(
() => _opdrachtgeversCache
.Where(x =>
x.vNaam.Contains(FirstName)
&& x.vLastName.Contains(LastName)
)
);
}
答案 0 :(得分:3)
如果您将异步任务变为Observable,这看起来像是Switch
的经典用法:
this.WhenAnyValue(x => x.FirstName,
x => x.LastName
)
.Throttle(TimeSpan.FromMilliseconds(100))
.Select(l => PopulateGrid().ToObservable())
.Switch()
.Subscribe(vm => Results = vm);
在用户输入时,应使用 Throttle
来禁止呼叫。所以根据需要调整TimeSpan。
答案 1 :(得分:0)
如果我理解你想要什么,可以用一种非常直接的方式来完成,如果你稍微重构一下代码就可以清理。
首先,将名字和姓氏触发到observables。在下面的代码中,我使用了主题,但如果您能够使用静态Observable方法来转换&#39;则会更好。他们成为可观察者;例如Observable.FromEvent
。
然后转动代码将结果提取到一个observable中。在下面的代码中,我使用了Observable.Create
来返回IEnumerable<string>
的流。
最后,您可以使用Switch运算符订阅每个新的GetResults调用,并取消之前对GetResults的调用。
听起来很复杂,但代码很简单:
private Subject<string> _firstName = new Subject<string>();
private Subject<string> _lastName = new Subject<string>();
private Task<IEnumerable<string>> FetchResults(string firstName, string lastName, CancellationToken cancellationToken)
{
// Fetch the results, respecting the cancellation token at the earliest opportunity
return Task.FromResult(Enumerable.Empty<string>());
}
private IObservable<IEnumerable<string>> GetResults(string firstName, string lastName)
{
return Observable.Create<IEnumerable<string>>(
async observer =>
{
// Use a cancellation disposable to provide a cancellation token the the asynchronous method
// When the subscription to this observable is disposed, the cancellation token will be cancelled.
CancellationDisposable disposable = new CancellationDisposable();
IEnumerable<string> results = await FetchResults(firstName, lastName, disposable.Token);
if (!disposable.IsDisposed)
{
observer.OnNext(results);
observer.OnCompleted();
}
return disposable;
}
);
}
private void UpdateGrid(IEnumerable<string> results)
{
// Do whatever
}
private IDisposable ShouldUpdateGridWhenFirstOrLastNameChanges()
{
return Observable
// Whenever the first or last name changes, create a tuple of the first and last name
.CombineLatest(_firstName, _lastName, (firstName, lastName) => new { FirstName = firstName, LastName = lastName })
// Throttle these tuples so we only get a value after it has settled for 100ms
.Throttle(TimeSpan.FromMilliseconds(100))
// Select the results as an observable
.Select(tuple => GetResults(tuple.FirstName, tuple.LastName))
// Subscribe to the new results and cancel any previous subscription
.Switch()
// Use the new results to update the grid
.Subscribe(UpdateGrid);
}
快速提示:您应该将明确的调度程序传递到Throttle中,以便您可以使用TestScheduler有效地对此代码进行单元测试。
希望它有所帮助。