正确的方法来重复触发事件流的操作

时间:2017-09-21 19:50:35

标签: rx-java rx-java2

我是RxJava的新手,所以我仍然试图理解它。我有Observable代表按钮点击流,因此很热门。每次单击该按钮,我都想做一些I / O.如果失败了,我想重复并尝试再次进行I / O,直到成功为止。这似乎是使用retry()repeat()的绝佳机会,但这些只能用于热观察,而不是冷。

这里有一些伪代码可以实现我想要做的事情:

buttonRequests
   .map(actionEvent -> doIO())
   .repeatAboveIfFailedUntilIOSucceeds()
   .subscribe(...);

我考虑过使用flatMap来复制该事件,而不是使用skip忽略其余的事件,如果成功的话,但是我不能干净利落地得到一个不确定数量的事件。尝试。

考虑这个问题的正确方法是什么?

2 个答案:

答案 0 :(得分:1)

请看一下测试。在每个事件中都会触发一个新的IO-Request。 Switch-Map就像Flat-Map,但是当新的上游事件进来时,它将取消订阅最近的订阅。如果您正在使用并发,Flat-Map将启动一个新的订阅。因此,假设您的hot observable触发了一个事件,flatMap开始在另一个线程(subscribeOn)上执行您的IO工作。如果另一个事件进入,而最后一个事件仍在执行,它将开始执行另一个IO任务。 Switch-Map将取消订阅最后一个并为当前事件启动一个。让我们看一下retry() - 运算符。重试只会重新订阅由ioWorkWrapped'提供的观察结果。直到observable完成onComplete。这可能非常危险,因为想象它会在每次尝试时失败。它会永远旋转。建议使用指数退避'指数退避'并且在X尝试之后提供备份可观察失败。对于'重试的用法当'请看一下这本优秀的书: Reactive Programming with RxJava

public class LibraryTest {
    private AtomicInteger idx;

    @Before
    public void setUp() throws Exception {
        idx = new AtomicInteger(0);
    }

    @Test
    public void name() throws Exception {
        Observable<String> stringObservable = Observable.just(1)
                .switchMap(integer -> ioWorkWrapped()
                        .doOnError(throwable -> System.out.println("Something went wrong."))
                        .retry()
                );

        stringObservable.test()
                .await()
                .assertResult("value");


    }

    private Observable<String> ioWorkWrapped() {
        return Observable.defer(() -> {
            try {
                Thread.sleep(500); // IO Work
                if (idx.getAndIncrement() < 5) { // for testing...
                    return Observable.error(new IllegalStateException("Wurst"));
                }
                return Observable.just("value");
            } catch (Exception ex) {
                return Observable.error(ex);
            }
        });
    }
}

答案 1 :(得分:0)

如果I / O操作失败,您需要使用运算符retryWhen,您可以抛出在运算符中检查的Runnable异常。如果是这种类型的异常,你重试。

在此示例中,我们将重试4次。但是这种情况可以通过我们收到的throwable类型来改变。

int count=0;

@Test
public void retryWhenConnectionError() {
    Subscription subscription = Observable.just(null)
            .map(connection -> {
                System.out.println("Trying to open connection");
                connection.toString();
                return connection;
            })
            .retryWhen(errors -> errors.doOnNext(o -> count++)
                            .flatMap(t -> count > 3 ? Observable.error(t) :
                                    Observable.just(null).delay(100, TimeUnit.MILLISECONDS)),
                    Schedulers.newThread())
            .subscribe(s -> System.out.println(s));
    new TestSubscriber((Observer) subscription).awaitTerminalEvent(500, TimeUnit.MILLISECONDS);
}

您可以在此处查看更多示例https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/errors/ObservableExceptions.java