Как обрабатывать исключения из асинхронных методов внутри оператора SelectMany - PullRequest
5 голосов
/ 06 марта 2012

Я пытаюсь выполнить некоторые задачи асинхронно, используя 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?

Просто чтобы прояснить, окончательная реализация - это синхронный вызов функции, который может вызывать или не вызывать исключение.Цель состоит в том, чтобы передать его подписчику для дальнейшей обработки (в конкретном случае пользователю будет показано сообщение).

Редактировать

Я перенес свои выводы в ответ, чтобы пометить этот вопрос как ответивший.Лично я не согласен с автоответчиком ... но иногда нет другого пути, так что извините за это.

Ответы [ 2 ]

3 голосов
/ 07 марта 2012

Используйте Materialize для преобразования ваших сообщений OnError / OnCompleted в уведомления.

Например,

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

доставит вам сообщение об ошибке / завершении, завернутое вуведомление, которое должно быть обработано в вашей фактической точке подписки в обратном направлении, в отличие от ошибки, завершаемой внутри SelectMany.

Это полезно для большинства операций асинхронного вызова, поскольку метод либо завершается неудачей, либо завершается.

1 голос
/ 11 марта 2012

Ответ

На самом деле код - это , работающий правильно. Однако отладчик прерывается на исключениях, поскольку асинхронные операции все еще выполняются в фоновом режиме - по крайней мере те, которые уже были запущены, когда возникло первое исключение. Бросил меня! Если вы запустите код без отладчика, исключения будут проглочены. Итак, я думаю, что проблема действительно была перед компьютером: -)

Еще некоторые пояснения по 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");
    }
}
...