Rx Observable 能否优雅地处理运算符中的异常并继续?

Can an Rx Observable gracefully handle exceptions in an operator and continue?

即通过传递错误条件并且停止整个Observable?

My Observable 以用户提供的常见递送服务(FedEx、UPS、DHL 等)包裹跟踪号码列表开始,在线查找预计递送日期,然后 returns 这些日期从今天算起的天数(即 "in 3 days" 而不是 "Jan 22")。问题是,如果任何单个查找导致异常,整个流都会停止,其余代码将不会被查找。没有能力优雅地处理,比如说,UnknownTrackingCode Exception,所以 Observable 不能保证它会查找用户提交的所有代码。

public void getDaysTillDelivery(List<String> tracking_code_list) {
        Observable o = Observable.from(tracking_code_list)
                   // LookupDeliveryDate performs network calls to UPS, FedEx, USPS web sites or APIs
                   // it might throw: UnknownTrackingCode Exception, NoResponse Exception, LostPackage Exception
                .map(tracking_code -> LookupDeliveryDate(tracking_code))
                .map(delivery_date -> CalculateDaysFromToday(delivery_date));
        o.subscribe(mySubscriber); // will handle onNext, onError, onComplete
}

由于一个错误而停止 Observable 流是设计

可以克服默认行为,但首先要消除 Rx 的许多优点:

有没有更好的方法来处理这个问题?

如果出现错误,您到底希望发生什么?您是只想丢弃该条目还是想让下游的某些东西对其进行处理?

如果你想让下游的东西采取一些行动,那么你实际上是在将错误转化为数据(有点像你的例子 returning 一个标记值 1899-12-31 来表示错误).这种策略的定义意味着下游的一切都需要了解数据流可能包含错误而不是数据,并且必须修改它们来处理它。

但是,您可以将 Observable 流变成 Either 值流,而不是产生一个神奇的值。该值要么是日期,要么是错误。下游的所有东西都会收到这个 Either 对象,并可以询问它是否有值或错误。如果它有一个值,他们可以用他们的计算结果生成一个新的 Either 对象。如果它有错误并且他们无法对其执行任何操作,则他们可以自己产生错误。

我不知道 Java 语法,但在 c# 中可能是这样的:

Observable.From(tracking_code_list)
    .Select(t =>
        {
            try { return Either.From(LookupDeliveryDate(t)); }
            catch (Exception e)
            {
                return Either.FromError<Date>(e);
            }
        })
     .Select(dateEither =>
        {
            return dateEither.HasValue ?
                Either.From(CalculateDaysFromToday(dateEither.Value)) :
                Either.FromError<int>(dateEither.Error);
        })
     .Subscribe(value =>
        {
            if (value.HasValue) mySubscriber.OnValue(value.Value);
            else mySubscribe.OnError(value.Error);
        });

您的另一个选择是 "handle"/在错误发生时抑制它。根据您的需要,这可能就足够了。在这种情况下,只需 LookupDeliveryDate return 魔法日期而不是例外,然后添加 .filter 以在魔法日期到达 CalculateDaysFromToay.[=19= 之前过滤掉它们]