Dynamic delay value with repeatWhen()

后端 未结 5 1745

Right now I\'m implementing some polling logic with RxJava. I\'m supposed to poll an endpoint a number of times until it tells me to stop. Additionally, each response come

相关标签:
5条回答
  • 2020-12-19 03:37

    You could abuse retryWhen - but I'm just saying it is possible, not that you should do it:

    package com.example.retrywhen;
    
    import com.example.LoggingAction1;
    
    import org.pcollections.PVector;
    import org.pcollections.TreePVector;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicInteger;
    
    import rx.Observable;
    import rx.functions.Action0;
    import rx.functions.Func0;
    import rx.functions.Func1;
    import rx.schedulers.Schedulers;
    
    import static com.example.Utils.sleep;
    
    public class RetryWhenDynamicDelayTest {
    
        final static PVector<Integer> delays = TreePVector.<Integer>singleton(500).plus(1_000).plus(2_000);
    
        final static AtomicInteger count = new AtomicInteger(0);
    
        final static Observable<Integer> willCycleThroughTheList = Observable.defer(new Func0<Observable<Integer>>() {
            @Override
            public Observable<Integer> call() {
                return Observable.just(delays.get(count.getAndIncrement() % 3));
            }
        });
    
        static class ThisIsNotReallyAnException extends Throwable {
            final Integer integer;
    
            ThisIsNotReallyAnException(Integer integer) {
                this.integer = integer;
            }
        }
    
    
        public static void main(String[] args) {
    
            final long now = System.currentTimeMillis();
    
            willCycleThroughTheList.flatMap(new Func1<Integer, Observable<?>>() {
                @Override
                public Observable<?> call(Integer integer) {
                    return Observable.error(new ThisIsNotReallyAnException(integer));
                }
            })
                    .doOnUnsubscribe(new Action0() {
                        @Override
                        public void call() {
                            System.out.println("Millis since start: " + (System.currentTimeMillis() - now));
                        }
                    })
                    .onErrorResumeNext(new Func1<Throwable, Observable<Integer>>() {
                        @Override
                        public Observable<Integer> call(Throwable throwable) {
                            if (throwable instanceof ThisIsNotReallyAnException) {
                                ThisIsNotReallyAnException thisIsNotReallyAnException = (ThisIsNotReallyAnException) throwable;
                                return Observable.just((thisIsNotReallyAnException.integer)).concatWith(Observable.error(throwable));
                            } else {
                                return Observable.error(throwable);
                            }
                        }
                    })
                    .retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
                        @Override
                        public Observable<?> call(Observable<? extends Throwable> observable) {
                            return observable.flatMap(new Func1<Throwable, Observable<?>>() {
                                @Override
                                public Observable<?> call(Throwable throwable) {
                                    if (throwable instanceof ThisIsNotReallyAnException) {
                                        ThisIsNotReallyAnException thisIsNotReallyAnException = (ThisIsNotReallyAnException) throwable;
                                        return Observable.timer(thisIsNotReallyAnException.integer, TimeUnit.MILLISECONDS);
                                    } else {
                                        return Observable.error(throwable);
                                    }
                                }
                            });
                        }
                    })
                    .subscribeOn(Schedulers.io())
                    .subscribe(new LoggingAction1<Object>(""));
    
            sleep(10_000);
        }
    }
    

    Prints:

    Millis since start: 75
    call (): 500
    Millis since start: 590
    call (): 1000
    Millis since start: 1591
    call (): 2000
    Millis since start: 3593
    call (): 500
    Millis since start: 4094
    call (): 1000
    Millis since start: 5095
    call (): 2000
    Millis since start: 7096
    call (): 500
    Millis since start: 7597
    call (): 1000
    Millis since start: 8598
    call (): 2000
    
    0 讨论(0)
  • 2020-12-19 03:38

    This is the solution I ended up using:

    public static Observable<PollResponse> createPollObservable(RetrofitService service, PollResponse response) {
        return Blah::shouldStopPolling(response)
            ? Observable.empty()
            : service
                .pollEndpoint()
                .delaySubscription(getPollDelay(response), TimeUnit.MILLISECONDS)
                .concatMap(response1 -> createPollObservable(service, response1)
                        .startWith(response1)
                        .takeUntil(Blah::shouldStopPolling)
                );
    }
    

    It instead uses recursion to always have the latest PollResponse object and also switches to delaySubscription() rather than repeatWhen().

    0 讨论(0)
  • 2020-12-19 03:46

    As @JohnWowUs mentioned, you need out-of-band communication, but if you subscribe to the sequence more than once, you can use defer to have per-subscriber state:

    Observable.defer(() -> {
        int[] pollDelay = { 0 };
        return service.pollEndpoint()
        .doOnNext(response -> pollDelay[0] = response.getDelay())
        .repeatWhen(o -> o.flatMap(v -> Observable.timer(pollDelay[0], MILLISECONDS)))
        .takeUntil(Blah::shouldStopPolling);
    });
    
    0 讨论(0)
  • 2020-12-19 03:46
    service.endpoint()
     .flatMap((Function<Response<Void>, SingleSource<?>>) response -> Single.just(response).delaySubscription(getDelayFromResponce(response), TimeUnit.MILLISECONDS))
                .repeat()
                .subscribe();
    
    0 讨论(0)
  • 2020-12-19 03:55

    You could use the side effect operator doOnNext to update a delay variable and then use that in your repeatWhen

    int pollDelay = 5000;
    
    service.pollEndpoint()
    .doOnNext(pollResponse -> pollDelay=pollResponse.getDelay())
    .repeatWhen(observable -> observable.delay(pollDelay, TimeUnit.MILLISECONDS))
    .takeUntil(Blah::shouldStopPolling);
    
    0 讨论(0)
提交回复
热议问题