7

经典实时搜索示例:

var searchResults = from input in textBoxChanged
                    from results in GetDataAsync(input)
                    select results;

GetDataAsync 返回:

 Task<List<DataRecord>>

这显然是一个竞争条件,因此如果由第二个输入触发的搜索在第一个输入之前返回,则第一个输入的结果在后面,因此给了我错误的数据。

我一直在读到 .Switch() 运算符会神奇地解决这个问题,但是如何?

.Switch() 仅存在于:

 IObservable<IObservable<T>>
4

1 回答 1

16

假设

我将假设 textBoxChanged 是由以下内容创建的:

var textBoxChanged = Observable.FromEventPattern(x, "TextChanged")
                               .Select(evt => ((TextBox)evt.Sender).Text);

使用 SelectMany 时防止竞争条件

from... from... 在 LINQ 理解中转换为 a SelectMany,这就是您正在使用的。Rx 足够聪明,可以将Task<List<DataRecord>>返回的 byGetDataAsync(input)转换为IObservable<List<DataRecord>>.

问题是您希望防止从除最近提交的搜索请求之外的所有搜索请求中返回结果。

为此,您可以利用TakeUntil. 它具有以下签名:

public static IObservable<TSource> TakeUntil<TSource, TOther>(
    this IObservable<TSource> source,
    IObservable<TOther> other
)

它从源可观察序列返回值,直到另一个可观察序列产生值。

我们可以这样使用它:

var searchResults = from input in textBoxChanged
                    from results in GetDataAsync(input).ToObservable().TakeUntil(textBoxChanged)
                    select results;

这将防止竞争条件,但也会订阅两次 textBoxChanged。

Switch改为使用

这是一种非常有用的模式,因此使用运算符引入了另一种方法,该Switch()运算符也负责双重订阅。

而不是使用SelectMany,只需将输入直接投影到搜索查询中 - 这将给出一个返回类型IObservable<IObservable<List<DataRecord>>,一个流的流。Switch 会从一个流跳到另一个流,只返回最近的流。这相当于 SelectMany/TakeUntil 组合:

var searchResults = (from input in textBoxChanged
                     select GetSearchResults(input).ToObservable())
                    .Switch();

我强烈建议查看 Rx Hands on Lab,它更详细地解释了这一点。

于 2013-11-03T15:20:48.443 回答