观察者<t> .OnError的假设似乎与Stubs.Throw </t>不一致

时间:2013-01-21 00:13:33

标签: system.reactive

IObservable&lt; T&gt; .Subscribe上的一个重载是

public static IDisposable Subscribe<T>(this IObservable<T> source, Action<T> onNext)

在内部,这会在IObservable上创建并注册AnoymousObserver<T>。未指定的onError参数设置为Stubs.Throw,这是一个简单的lambda,可以重新传入传入的异常。

在内部,IObservable的观察者都包含在Observer类型的单个实例中。这是Observer<T>.OnError

中的代码
public void OnError(Exception error)
{
    foreach (var observer in _observers.Data)
        observer.OnError(error);
}

由于Stubs.Throw抛出异常,来自该观察者的OnError的异常通过foreach循环向上传递,而_observers.Data中的其他观察者从未调用它们的OnError。异常本身被Rx内部某处吞噬。

在我看来,Observer&lt; T&gt; .OnError应该在try-catch中包装observer.OnError,或者Stubs.Throw应该吞下异常而不是抛出异常。通过不传递onError参数,IObservable.Subscribe的用户希望仅针对该订阅忽略该错误。使用自己的onError回调注册的其他订阅者应该不受影响。

(我已经在Codeplex上提交了一个bug,但是跟踪器看起来很冷清,所以我想我也要问我是否正确理解这一点。)


更新:阿斯蒂的回答是正确的。在关于链接错误报告的讨论中,davedev给出了IEnumerable的类比,以及如何抛出未处理的异常的默认值相当于在没有catch块的情况下迭代IEnumerable的默认值。

如果observable确定不应将特定异常视为致命异常,则不应使用OnError进行通信。 OnError不能保证执行,因为调用它意味着observable首先出现严重错误。相反,可观察量可以被定义为IObservable&lt; Either&lt; T,Exception&gt;&gt; (Observable2.Retry中的示例用法)。

1 个答案:

答案 0 :(得分:1)

这实际上是预期的行为。

OnError不是一个被调用的方法,表示在观察者OnNext上抛出异常 - 它表示可观察序列的异常终止。当通知转换出Observable monad时,默认的OnError实现将抛出异常。

如果您阅读Rx Design Guidelines,您将更清楚地了解Rx合同和异常处理。至于管道中的错误处理以及更多类似IEnumerable / IQueryable的行为,请查看SubscribeSafe