Classic live search example:
var searchResults = from input in textBoxChanged
from results in GetDataAsync(input)
select results;
GetDataAsync returns a:
Task<List<DataRecord>>
Here is obviously a race condition so if the search triggered by the second input returns before the first one, the results from the first input comes after and therefore gives me the wrong data.
I keep reading everywhere that the .Switch() operator will magically solve this, but how?
.Switch() only exists on:
IObservable<IObservable<T>>
I will assume that textBoxChanged has been created by something like:
var textBoxChanged = Observable.FromEventPattern(x, "TextChanged")
.Select(evt => ((TextBox)evt.Sender).Text);
from... from... in a LINQ comprehension translates to a SelectMany
, which is what you are using. Rx is smart enough to translate the Task<List<DataRecord>>
returned by GetDataAsync(input)
into an IObservable<List<DataRecord>>
.
The problem is that you want to prevent results coming back from all but the most recently submitted search request.
To do this, you can leverage TakeUntil
. It has the following signature:
public static IObservable<TSource> TakeUntil<TSource, TOther>(
this IObservable<TSource> source,
IObservable<TOther> other
)
And it returns the values from the source observable sequence until the other observable sequence produces a value.
We can employ it like this:
var searchResults = from input in textBoxChanged
from results in GetDataAsync(input).ToObservable().TakeUntil(textBoxChanged)
select results;
This will prevent the race condition, but will also subscribe twice to textBoxChanged.
Switch
insteadIt is such a useful pattern that an alternative approach was introduced using the Switch()
operator which also takes care of the double subscription.
Instead of using SelectMany
, simply project the input directly into the search query - this will give a return type of an IObservable<IObservable<List<DataRecord>>
, a stream of streams. Switch will jump from stream to stream only returning the most recent stream. This is the equivalent of the SelectMany/TakeUntil combo:
var searchResults = (from input in textBoxChanged
select GetSearchResults(input).ToObservable())
.Switch();
I highly suggest looking at the Rx Hands on Lab that explains this in much more detail.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With