简体   繁体   English

如何在SelectMany语句中处理异步方法的异常

[英]How to handle exceptions from asynchronous methods within a SelectMany statement

I am trying to process some tasks asynchronously using Rx, eg 我试图使用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"); }
    );

The problem I have with this code is that the exception is not passed to the subscriber. 我对此代码的问题是异常未传递给订阅者。 So, after a lot of trying I gave up and decided to ask this simple question: 所以,经过大量的尝试,我放弃了,并决定问这个简单的问题:

How do you handle the exceptions raised from within asynchronous methods within a SelectMany statement? 如何处理SelectMany语句中异步方法中引发的异常?

Just to make it clear, the final implementation is a synchroneous function call that may or may not throw an exception. 为了说清楚,最终的实现是一个同步的函数调用,可能会或可能不会抛出异常。 The goal is to pass it on to the subscriber so that it can be further processed (in the specific case a message will be shown to the user). 目标是将其传递给订户,以便可以进一步处理(在特定情况下,将向用户显示消息)。

Edit 编辑

I moved my findings down to an answer, so that I can mark this question as answered. 我将我的发现归结为一个答案,以便我可以将这个问题标记为已回答。 Personally, I do not agree with self answering ... but sometimes there is no other way, so sorry for it. 就个人而言,我不同意自我回答......但有时没有别的办法,所以很抱歉。

Use Materialize to convert your OnError / OnCompleted messages into notifications. 使用Materialize将OnError / OnCompleted消息转换为通知。

For example, 例如,

observable.SelectMany(x => Observable.Start(fn).Materialize())

will get you the error / completion wrapped in a notification to be handled in your actual subscription point way down the line, as opposed to the error being terminated inside the SelectMany. 将获得包含在通知中的错误/完成,以便在您的实际订阅点方式处理,而不是在SelectMany内终止错误。

This is useful for most Async call operations because the method either fails or completes. 这对大多数异步调用操作很有用,因为该方法失败或完成。

The answer 答案

Actually the code is working correctly. 实际上,代码工作正常。 However, the debugger breaks at the exceptions as the async operations are still executed in the background - well at least those that were already started when the first exception occurred. 但是,由于异步操作仍在后台执行,调试器会在例外情况下中断 - 至少是在第一个异常发生时已经启动的那些操作。 Threw me! 扔了我! If you run the code without debugger the exceptions are swallowed.So I guess the problem was really in front of the computer :-) 如果您在没有调试器的情况下运行代码,则会吞下异常。所以我猜问题实际上是在计算机前:-)

Still some clarifications on the Observable.Start as I assumed - an this correctly - that the implemtation should have actually some error handling implemented ... see Background. 关于Observable.Start一些澄清,正如我所假设的 - 这是正确的 - 实现应该实际上实现了一些错误处理...参见背景。

Background 背景

Observable.Start is a convenience method that uses the Observable.ToAsync method to turn a function/acion into an async operation. Observable.Start是一种方便的方法,它使用Observable.ToAsync方法将函数/ acion转换为异步操作。 If you look at the implementation of the method you'll see that it already does the exception handling/forwarding. 如果你看一下该方法的实现,你会发现它已经进行了异常处理/转发。

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");
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM