我正在尝试使用Rx异步处理某些任务,例如
var list = Enumerable.Range(0, 100)
.ToObservable()
.SelectMany(x => Observable.Start(() => {
Console.WriteLine("Processing {0} ...", x);
Thread.Sleep(100 * x % 3);
if (x > 90) {
Console.WriteLine("Procesing exception {0} > 90", x);
throw new Exception("Value too large");
}
Console.WriteLine("Processing {0} completed.", x);
return x;
}))
.Subscribe(
x => { Console.WriteLine("Next [{0}]", x); },
e => {
Console.WriteLine("Exception:");
Console.WriteLine(e.Message);
},
() => { Console.WriteLine("Complete"); }
);
我对此代码的问题是异常未传递给订阅者。所以,经过大量的尝试,我放弃了,并决定问这个简单的问题:
如何处理SelectMany
语句中异步方法中引发的异常?
为了说清楚,最终的实现是一个同步的函数调用,可能会也可能不会抛出异常。目标是将其传递给订户,以便可以进一步处理(在特定情况下,将向用户显示消息)。
修改
我将我的发现归结为一个答案,以便我可以将这个问题标记为已回答。就个人而言,我不同意自我回答......但有时没有别的办法,所以很抱歉。
答案 0 :(得分:3)
使用Materialize
将OnError / OnCompleted消息转换为通知。
例如,
observable.SelectMany(x => Observable.Start(fn).Materialize())
将在您的实际订阅点中向您提供包含在通知中的错误/完成,而不是在SelectMany中终止错误。
这对大多数异步调用操作很有用,因为该方法失败或完成。
答案 1 :(得分:1)
答案
实际上代码 正常工作。但是,由于异步操作仍在后台执行,调试器会在例外情况下中断 - 至少是在第一个异常发生时已经启动的那些操作。扔了我!如果您在没有调试器的情况下运行代码,则会吞下异常。所以我猜问题实际上是在计算机前: - )
正如我所假设的那样Observable.Start
仍然有一些澄清 - 这是正确的 - 实现应该实际上实现了一些错误处理...参见背景。
<强>背景强>
Observable.Start
是一种便捷方法,它使用Observable.ToAsync
方法将函数/动作转换为异步操作。如果你看一下该方法的实现,你会发现它已经进行了异常处理/转发。
public static Func<IObservable<TResult>> ToAsync<TResult>(this Func<TResult> function, IScheduler scheduler) {
if (function != null) {
if (scheduler != null) {
return () => {
AsyncSubject<TResult> asyncSubject = new AsyncSubject<TResult>();
scheduler.Schedule(() => {
TResult result = default(TResult);
try {
result = function();
} catch (Exception exception1) {
Exception exception = exception1;
asyncSubject.OnError(exception);
return;
}
asyncSubject.OnNext(result);
asyncSubject.OnCompleted();
});
return asyncSubject.AsObservable<TResult>();
};
} else {
throw new ArgumentNullException("scheduler");
}
} else {
throw new ArgumentNullException("function");
}
}