Search code examples
javarx-javareactivex

How to have a delay occur POST-emission?


I would like for every emission from my Observable to have a delay occur AFTER its emission. i.e. For every item emitted from Observable.fromIterable(listOf(1,2,3)) I would like a variable/dynamic delay AFTERWARDS.

i.e. given a list of timers or delays [3, 5, 10], I would like for the following timeline to occur with my observable:

1 -> Wait 3 seconds -> 2 -> Wait 5 seconds -> 3 -> Wait 10 seconds

Please note that this is NOT the same as:

Wait 3 seconds -> 1 -> Wait 5 seconds -> 2 -> Wait 10 seconds -> 3

which can be easily achieved with .zip + .delay or Observable.Timer


Solution

  • Using zip() is the right way to go. When you want the delay after the first value is emitted, then you can use startWith() to add a zero delay for the first combination. The code might look like this:

    Observable<Integer> values = Observable.fromArray(1,2,3,4,5);
    Observable<Long> delays = Observable.fromArray(300L, 400L, 500L, 600L);
        
    Observable<Integer> delayedValues = values.zipWith(
        delays.startWith(0L),
        new BiFunction<Integer, Long, Integer>() {
            @Override
            public Integer apply(Integer v, Long d) throws Exception {
                Thread.sleep(d);
                return v;
            }
        }
    );
    System.out.println("Begin subscribing");
    long startTime = System.currentTimeMillis();
    delayedValues.subscribe(v -> {
        long currentTime = System.currentTimeMillis();
        long diff = currentTime - startTime;
        System.out.println("["+diff+"] "+v);
    });
    System.out.println("After subscribing");
    

    This will generate the following output:

    Begin subscribing
    [35] 1
    [336] 2
    [736] 3
    [1236] 4
    [1836] 5
    After subscribing
    

    As you see, the first value is printed immediately, following by a 300ms delay for the next value until you reached the end.