我需要对可观察的完成/失败执行异步方法(例如清理作业)。此外,如果清理方法失败,则可观察链也应该失败。
有没有一种标准的方法可以做到这一点?
假设我有一个序列Observable source和asyncCleanup()方法,它返回一个可观察的清理结果。
doOnCompleted / doOnTerminate / doOnUnsubscribe等副作用方法似乎不合适:
source.doOnUnsubscribe(()->asyncCleanup().subscribe());
即使asyncCleanup()失败,observable链也会成功。 所以asyncCleanup()应该是同一个链的一部分。
我想出的最好成绩如下:
source.onErrorResumeNext(err->asyncCleanup()
.flatMap(cleanupResult->Observable.error(err)))
.concatWith(asyncCleanup()
.flatMap(cleanupResult->Observable.empty()));
如果失败,onErrorResumeNext将调用asyncCleanup()并将映射回原始错误。 如果成功,我们可以与映射到空序列的asyncCleanup()连接。 不幸的是,如果下游有一个take()或类似的限制运算符,它就无法工作,这可能会导致连接的observable甚至不能订阅。
更新2017-08-01 : 问题是关于序列可观察的。对于单项可观察源,解决方案非常简单:
singleItemSource
.onErrorResumeNext(err->asyncCleanup()
.flatMap(cleanupResult->Observable.error(err)))
.flatMap(single->asyncCleanup()
.map(cleanupResult->single));
答案 0 :(得分:1)
您提出的想法违反了Observable
合同,因为:
Rx设计指南 ,6.8 - 取消订阅不应抛出
我想到的唯一解决问题的方法是将asyncCleanup()
转换为前一个序列的相同类型,如果成功返回例如Observable.empty(),则可以使用concat
运营商。
public Observable<Long> asyncCleanup() {
...
return Observable.empty();
}
//Concat asyncCleanup() to be executed after the sequence
Observable.rangeLong(1, 10)
.concatWith(asyncCleanup())
.subscribe(System.out::println, System.out::println);
希望它有所帮助...
答案 1 :(得分:1)
有一个flatMap
重载,您可以将上游的onXXX
事件映射到Observable
s:
Observable.range(1, 10)
.flatMap(Observable::just, e -> asyncCleanup(), () -> asyncCleanup())
.subscribe(...);
答案 2 :(得分:0)
我评论avobe的另一个解决方案是工厂方法Observable.using
。这是一个简单的例子:
Observable<Long> range = Observable.rangeLong(1, 10);
Observable.using(() -> range, //The resource provider
observable -> observable, //The resource
observable -> asyncCleanup() //The resource disposer
.subscribe(System.out::println, System.out::println))
.subscribe(System.out::println, System.out::println);
这里的缺点是你应该处理asyncCleanup()
成功和错误,而不会传播或抛出。使用这种方法,您应该考虑在asyncCleanup()
方法的情况下远离Observable。