Rx Observable可以优雅地处理运算符中的异常并继续吗?

时间:2015-01-19 19:17:56

标签: system.reactive reactive-programming rx-java

,即通过传递错误条件而停止整个Observable?

My Observable以用户提供的常用送货服务(FedEx,UPS,DHL等)包裹追踪号码列表开始,在线查找预计送达日期,然后按今天的天数返回这些日期(即“在3天内”而不是“1月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的许多好处:

  • 我可以打包LookupDeliveryDate,因此它会返回日期代替例外(例如1899-12-31的{​​{1}}),但这会阻止“松散耦合的代码”,因为UnknownTrackingCode Exception会需要处理这些特殊情况
  • 我可以使用CalculateDaysFromToday和块来包围每个匿名函数,但这实际上阻止了我使用lambdas
  • 我可以使用try/catch来指导代码路径,但这可能需要维护某些状态并消除确定性评估
  • 显然,每个步骤的错误处理都会阻止合并if/thens
  • 中的所有错误处理
  • 编写我自己的错误处理操作符是可能的,但记录很少

有没有更好的方法来解决这个问题?

1 个答案:

答案 0 :(得分:4)

如果出现错误,您想要发生什么?你只是想把这个条目扔掉,还是想让下游的东西用它做点什么?

如果你想让下游的东西采取某些行动,那么你实际上是将错误转化为数据(有点像返回标记值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);
        });

你的另一个选择是&#34;句柄&#34; /在发生错误时抑制错误。根据您的需要,这可能就足够了。在这种情况下,只需让LookupDeliveryDate返回魔术日期而不是例外,然后添加.filter以过滤魔术日期,然后才能到达CalculateDaysFromToay