我遇到的情况是我正在处理的任务列表(启用驱动器,更改位置,等待停止,禁用)。
'等待'监视IObservable<Status>
,我想等待(所以我可以通过ContinueWith
和其他任务)。
我开始使用以下任务在中对订阅者的OnNext处理,但这只是丑陋。我现在提出的是这种扩展方法:
public static Task<T> WaitFor<T>(this IObservable<T> source, Func<T, bool> pred)
{
var tcs = new TaskCompletionSource<T>();
source
.Where(pred)
.DistinctUntilChanged()
.Take(1) //OnCompletes the observable, subscription will self-dispose
.Subscribe(val => tcs.TrySetResult(val),
ex => tcs.TrySetException(ex),
() => tcs.TrySetCanceled());
return tcs.Task;
}
(更新,svick建议处理OnCompleted
和OnError
)
Where
和DistinctUntilChanged
的顺序是否正确? (我认为他们是)答案 0 :(得分:11)
至少我会将此扩展方法更改为:
public static Task<T> WaitFor<T>(this IObservable<T> source, Func<T, bool> pred)
{
return
source
.Where(pred)
.DistinctUntilChanged()
.Take(1)
.ToTask();
}
使用.ToTask()
比引入TaskCompletionSource
要好得多。您需要引用System.Reactive.Threading.Tasks
命名空间才能获得.ToTask()
扩展方法。
此外,DistinctUntilChanged
在此代码中是多余的。您只获得一个值,因此默认情况下它必须是不同的。
现在,我的下一个建议可能有点争议。这个扩展是一个坏主意,因为它隐藏了正在发生的事情的真实语义。
如果我有这两个代码片段:
var t = xs.WaitFor(x => x > 10);
或者:
var t = xs.Where(x => x > 10).Take(1).ToTask();
我通常更喜欢第二个snippit,因为它清楚地告诉我发生了什么 - 我不需要记住WaitFor
的语义。
除非您将WaitFor
的名称更具描述性 - 也许TakeOneAsTaskWhere
- 否则您将清楚地使用运算符,而不是使用运算符并使代码更难管理。 / p>
以下内容是否更容易记住语义?
var t = xs.TakeOneAsTaskWhere(x => x > 10);
对我来说,最重要的是Rx运算符应该是组合的,而不是封装的,但是如果你要封装它们,那么它们的含义必须清楚。
我希望这会有所帮助。
答案 1 :(得分:3)
不是100%肯定这一点,但是从阅读Rx 2.0 beta博客文章,我认为如果你可以使用async / await,你可以“返回等待source.FirstAsync(pred)”或没有异步,“返回source.FirstAsync(预解码值).ToTask()“
http://blogs.msdn.com/b/rxteam/archive/2012/03/12/reactive-extensions-v2-0-beta-available-now.aspx