使用反应 + 限制调用异步方法的最佳方法是什么
本文关键字:异步方法 最佳 方法 是什么 调用 | 更新日期: 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>
传递给可观察对象来激活查询,或者您可以使用其重载,允许您单独提供OnNext
、OnError
和/或OnCompleted
处理程序,Rx 将其转换为您的IObserver<T>
。它是接收查询通知的IObserver<T>
。
有一个无参数的Subscribe
重载,它在内部使用静默观察器,目的是仅针对其副作用启动查询。例如,在您的情况下,如果您要使用 SelectMany
来完成加载建议的所有工作,并且您不需要单独的IObserver<T>
,那么您将通过调用 Subscribe
的无参数重载来启动查询。
在大多数情况下,不应使用 Subscribe
的无参数重载。Subscribe
的要点是,传递给它的IObserver<T>
(或单个处理程序)旨在引起查询的副作用。通过仅在Subscribe
或 Do
运算符(例如)中引起副作用,查询更容易推理和维护。
但是,有一种相当常见的方案,其中使用 Subscribe
的无参数重载是有意义的:如果查询的副作用是由异步方法引起的,则最好将SelectMany
与 Subscribe
的无参数重载一起使用。
原因很简单:SelectMany
是顺序组合运算符,它使您能够调用异步方法作为查询中的顺序步骤。因此,SelectMany
取消订阅与取消异步计算联系起来。释放订阅(由调用 Subscribe
返回的IDisposble
表示)会导致SelectMany
运算符的特殊异步重载提供的CancellationToken
发出取消信号。异步方法可以监视CancellationToken
以提前退出其计算。
没有任何接受异步观察器的Subscribe
重载,因此OnNext
返回Task
。但是,即使在 OnNext
处理程序中调用返回 void 的异步方法,在释放订阅时也不会发出异步方法信号。
请注意,无论如何,您的两个代码示例都略有错误。第一个示例不需要async
和await
关键字。如上所述,有接受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
并对返回值执行某些操作,例如日志记录。