web-dev-qa-db-ja.com

tryWhen:sの再試行がなくなるとエラーが発生します

RetryWhenのドキュメント の例では、次のようになります。

_Observable.create((Subscriber<? super String> s) -> {
  System.out.println("subscribing");
  s.onError(new RuntimeException("always fails"));
}).retryWhen(attempts -> {
  return attempts.zipWith(Observable.range(1, 3), (n, i) -> i).flatMap(i -> {
      System.out.println("delay retry by " + i + " second(s)");
      return Observable.timer(i, TimeUnit.SECONDS);
  });
}).toBlocking().forEach(System.out::println);
_

しかし、再試行がなくなった場合、どうすればエラーを伝播できますか?

.doOnError(System.out::println)afterを追加しても、retryWhen句はエラーをキャッチしません。それも放出されていますか?

.doOnError(System.out::println)beforeretryWhenを追加すると、すべての再試行に対して_always fails_が表示されます。

16
Theodor

retryWhenのドキュメントには、onError通知をサブスクライバーに渡して終了すると記載されています。したがって、次のようなことができます。

    final int ATTEMPTS = 3;

    Observable.create((Subscriber<? super String> s) -> {
        System.out.println("subscribing");
        s.onError(new RuntimeException("always fails"));
    }).retryWhen(attempts -> attempts
            .zipWith(Observable.range(1, ATTEMPTS), (n, i) ->
                    i < ATTEMPTS ?
                            Observable.timer(i, SECONDS) :
                            Observable.error(n))
            .flatMap(x -> x))
            .toBlocking()
            .forEach(System.out::println);
14

Javadoc for retryWhenは、次のように述べています。

そのObservableがonCompleteまたはonErrorを呼び出す場合、再試行は子サブスクリプションでonCompletedまたはonErrorを呼び出します。

簡単に言うと、例外を伝播する場合は、十分な再試行が完了したら、元の例外を再スローする必要があります。

簡単な方法は、_Observable.range_を再試行する回数より1大きい値に設定することです。

次に、Zip関数で、現在の再試行回数をテストします。 NUMBER_OF_RETRIES + 1に等しい場合は、Observable.error(throwable)を返すか、例外を再スローします。

例えば

_Observable.create((Subscriber<? super String> s) -> {
            System.out.println("subscribing");
            s.onError(new RuntimeException("always fails"));
        }).retryWhen(attempts -> {
            return attempts.zipWith(Observable.range(1, NUMBER_OF_RETRIES + 1), (throwable, attempt) -> {
                if (attempt == NUMBER_OF_RETRIES + 1) {
                    throw Throwables.propagate(throwable);
                }
                else {
                    return attempt;
                }
            }).flatMap(i -> {
                System.out.println("delaying retry by " + i + " second(s)");
                return Observable.timer(i, TimeUnit.SECONDS);
            });
        }).toBlocking().forEach(System.out::println);
_

余談ですが、doOnErrorはObservableにまったく影響を与えません。エラーが発生した場合に、何らかのアクションを実行するためのフックを提供するだけです。一般的な例はロギングです。

7
Will

1つのオプションは、Observable.materialize()を使用してObservable.range()アイテムを通知に変換することです。次に、onCompleted()が発行されると、エラーをダウンストリームに伝播できます(以下のサンプルでは、​​Pairを使用してObservable.range()通知とObservableからの例外をラップしています)

   @Test
   public void retryWhen() throws Exception {

    Observable.create((Subscriber<? super String> s) -> {
        System.out.println("subscribing");
        s.onError(new RuntimeException("always fails"));
    }).retryWhen(attempts -> {
        return attempts.zipWith(Observable.range(1, 3).materialize(), Pair::new)
           .flatMap(notifAndEx -> {
            System.out.println("delay retry by " + notifAndEx + " second(s)");
            return notifAndEx.getRight().isOnCompleted()
                    ? Observable.<Integer>error(notifAndEx.getLeft())
                    : Observable.timer(notifAndEx.getRight().getValue(), TimeUnit.SECONDS);
        });
    }).toBlocking().forEach(System.out::println);
}

    private static class Pair<L,R> {
        private final L left;
        private final R right;

        public Pair(L left, R right) {
            this.left = left;
            this.right = right;
        }

        public L getLeft() {
            return left;
        }

        public R getRight() {
            return right;
        }
    }
1
m.ostroverkhov

Maven Centralにある rxjava-extrasRetryWhenビルダーを使用して、必要な動作を取得できます。最新バージョンを使用してください。

Observable.create((Subscriber<? super String> s) -> {
    System.out.println("subscribing");
    s.onError(new RuntimeException("always fails"));
}) 
.retryWhen(RetryWhen
   .delays(Observable.range(1, 3)
               .map(n -> (long) n), 
            TimeUnit.SECONDS).build())
.doOnError(e -> e.printStackTrace()) 
.toBlocking().forEach(System.out::println);
0
Dave Moten