使用反应 + 限制调用异步方法的最佳方法是什么

本文关键字:异步方法 最佳 方法 是什么 调用 | 更新日期: 2023-09-27 18:35:08

我正在尝试使用 Rx + ReactiveUI 解决我的第一个任务,并且正在寻找解决任务的最佳实践,显示一个输入框,一旦用户开始键入,就会显示建议。

根据下面的代码示例,异步加载建议的最佳方法是什么?使用Subscribe还是使用Select Many?还是有更好的方法来做到这一点?

     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

使用反应 + 限制调用异步方法的最佳方法是什么

无论

哪种方式,您都必须调用Subscribe

Rx 中的查询使用延迟求值,这意味着仅定义查询不会启动它。延迟计算允许您通过有条件地应用运算符来构建查询,仅定义一次查询并将其存储在字段中以供以后使用,或者在调用 Subscribe 之前传递引用而不会引起任何副作用。

如果不调用Subscribe查询将保持非活动状态。

Subscribe通过将IObserver<T>传递给可观察对象来激活查询,或者您可以使用其重载,允许您单独提供OnNextOnError和/或OnCompleted处理程序,Rx 将其转换为您的IObserver<T>。它是接收查询通知的IObserver<T>

有一个无参数的Subscribe重载,它在内部使用静默观察器,目的是仅针对其副作用启动查询。例如,在您的情况下,如果您要使用 SelectMany 来完成加载建议的所有工作,并且您不需要单独的IObserver<T>,那么您将通过调用 Subscribe 的无参数重载来启动查询。

在大多数情况下,不应使用 Subscribe 的无参数重载。Subscribe的要点是,传递给它的IObserver<T>(或单个处理程序)旨在引起查询的副作用。通过仅在SubscribeDo 运算符(例如)中引起副作用,查询更容易推理和维护。

但是,有一种相当常见的方案,其中使用 Subscribe 的无参数重载是有意义的:如果查询的副作用是由异步方法引起的,则最好将SelectManySubscribe 的无参数重载一起使用。

原因很简单:SelectMany是顺序组合运算符,它使您能够调用异步方法作为查询中的顺序步骤。因此,SelectMany取消订阅与取消异步计算联系起来。释放订阅(由调用 Subscribe 返回的IDisposble表示)会导致SelectMany运算符的特殊异步重载提供的CancellationToken发出取消信号。异步方法可以监视CancellationToken以提前退出其计算。

没有任何接受异步观察器的Subscribe重载,因此OnNext返回Task。但是,即使在 OnNext 处理程序中调用返回 void 的异步方法,在释放订阅时也不会发出异步方法信号。

请注意,无论如何,您的两个代码示例都略有错误。第一个示例不需要asyncawait关键字。如上所述,有接受Task<T>返回选择器函数的特殊SelectMany重载,以及为该函数提供CancellationToken的其他重载。您可能应该使用后者。

您的第二个示例不应编译,假设LoadSearchSuggestions返回Task 。(除非 ReactiveUI 库或你引用的其他库提供了接受Task返回函数的重载Subscribe,在这种情况下,你必须查阅他们的文档。

除非是后者,并假设查询的其余部分是正确的,否则您应该执行以下操作:

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

其中LoadSearchSuggestionsAsync定义如下:

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

请注意,单位在 Rx 中表示 void。这是必需的,因为返回非泛型Task的异步方法不能与SelectMany一起使用。如果您有实际数据要返回,则只需将Unit替换为数据类型即可。然后,还可以将OnNext处理程序传递给Subscribe并对返回值执行某些操作,例如日志记录。