What is the best way to call asynchronous methods using reactive power + throttle

I am trying to solve my first problem using Rx + ReactiveUI and I am looking for best practices to solve the problem by showing an input box that will display suggestions as soon as the user starts typing.

As per the code example below, what's the best way to load sentences asynchronously? Using Subscribe

or using Select Many

? Or is there a better way to do it beneth these two?

     this.SearchTerms = this.ObservableForProperty(x => x.SearchTerm)
            .Throttle(SuggestionThrottle, RxApp.MainThreadScheduler)
            .Value()
            .SelectMany(async s => await this.LoadSearchSuggestions(s));  // 1st Possibility

        this.SearchTerms.Subscribe(this.LoadSearchSuggestions);           // 2nd Possibility

      

+3


source to share


1 answer


You have to call Subscribe

anyway.

Queries in Rx use lazy evaluation, which means that simply defining the query does not trigger it. Lazy evaluation allows you to build a query by conditionally, define the query only once and store it in a field for later, or pass a link without any side effects until you call Subscribe

.

Without a call, Subscribe

your request will remain inactive.

Subscribe

activates the request by navigating to the observable tag IObserver<T>

, or you can use its overloads, which allow you to individually allocate handlers OnNext

, OnError

and / or OnCompleted

that Rx converts to IObserver<T>

for you. This is the IObserver<T>

one that receives request notifications.

There is a parameterless overload Subscribe

that internally uses a silent observer with the intention of starting a query just for its side effects. For example, in your case, if you used SelectMany

to do all the work of loading sentences and you didn't need a separate one IObserver<T>

, then you should run the query by invoking a parameterless overload Subscribe

.

In most cases, you shouldn't use parameterless overloading Subscribe

. The point Subscribe

is that IObserver<T>

(or the individual handlers) you pass to it are designed to cause side effects of your request to occur. By only causing side effects in Subscribe

or, for example, in a statement Do

, the query is much easier to reason about and maintain.

However, there is one fairly common scenario where using parameterless overloading Subscribe

makes sense: if the side effects of your request are caused by an asynchronous method, then using SelectMany

along with unnoticed overloading is Subscribe

better.

The reason is simple: it SelectMany

is a sequential composition operator that allows you to call an asynchronous method as a sequential step within your request. Therefore, SelectMany

links unsubscribing to canceling your asynchronous computation. Eliminating a subscription (presented IDisposble

that is returned from a call to Subscribe

) causes the one CancellationToken

provided by special asynchronous operator overloads SelectMany

to be signaled to be canceled. Your asynchronous method can control CancellationToken

to complete its evaluation earlier.



There are no overloads Subscribe

that an asynchronous observer takes and OnNext

returns as a result Task

. But even if you had to call a void-returning async method in your handler OnNext

, your async method won't be signaled when the subscription is posted.

Note that both of your code examples are slightly flawed. In the first example, there is no need for the keywords async

and await

. As stated above, there are special overloads SelectMany

that accept the Task<T>

-opening selector function and additional overloads that provide CancellationToken

this function. You should probably use the latter.

The second example shouldn't compile, assuming what it LoadSearchSuggestions

returns Task

. (If the ReactiveUI library, or some other library you link to, provides an overload Subscribe

that accepts a Task

-returning function , in which case you will have to consult its documentation.)

Disallowing the latter and assuming the rest of your request is correct, here's what you should do:

this.SearchTerms = this.ObservableForProperty(x => x.SearchTerm)
  .Throttle(SuggestionThrottle, RxApp.MainThreadScheduler)
  .Value()
  .SelectMany(LoadSearchSuggestionsAsync)
  .Subscribe();

      

where is LoadSearchSuggestionsAsync

defined as follows:

async Task<Unit> LoadSearchSuggestionsAsync(string term, CancellationToken cancel)
{
  ...
  return Unit.Default;
}

      

Note that Unit represents void in Rx. This is necessary because an asynchronous method that does not return a generic method Task

cannot be used with SelectMany

. If you have actual data to return instead, just replace Unit

with your data type. Then you can also pass a handler OnNext

to Subscribe

and do something with the return value, like logging.

+12


source







All Articles