4

I have a method that sends a request to get the status of a job and returns the status, it looks like this:

Mono<JobStatus> getJobStatus() {...}

The JobStatus object has a method JobStatus.isDone() which returns whether the pending job is done or not.

Is there a way for me to keep subscribing to the mono until JobStatus.isDone() is true? i.e. something like getJobStatus().keepSubscribingUntil(status -> status.isDone())

CowZow
  • 1,275
  • 2
  • 17
  • 36
  • ```getJobStatus()``` will wait for the next signal to invoke the subscriber. Reactor is all about push model than pull, so it works out of the box – Barath Jun 10 '19 at 05:32

2 Answers2

11

One option would be for the getJobStatus() Mono to only emit when the job is done, which might not necessarily be easy depending on how that Mono is currently implemented.

For polling, assuming the Mono polls each time you subscribe to it you could use repeatWhen paired with takeUntil:

getJobStatus()
    .repeatWhen(completed -> completed.delayElements(Duration.ofMillis(pollDelay))) //(1)
    .takeUntil(JobStatus::isDone) //(2)
    .last() //(3)

(1) repeatedly re-subscribe to the source Mono (this produces a Flux<JobStatus>)

(2) cancel the above repeat cycle as soon as the returned status is marked as done

(3) switch back to a Mono<JobStatus> which emits the last iteration's status (so the one that is marked as done)

Simon Baslé
  • 27,105
  • 5
  • 69
  • 70
  • Thanks for your expertise; this solution is great. We'll also investigate your first suggestion if its possible to only emit when the job is done. – CowZow Jun 10 '19 at 14:34
  • 2
    Hey, regarding the `.repeatWhen(...)` line, should it be: `.repeatWhen(completed -> completed.delayElements(Duration.ofMillis(pollDelay))` instead? I.e. the flux inside the repeatWhen must emit many elements, rather than just have a Mono.delay which will emit something only once? – CowZow Jun 10 '19 at 18:10
  • 1
    @CowZow just came back to this answer and came to the same conclusion as you :) better late than never, I've fixed it – Simon Baslé Apr 26 '21 at 11:56
2

Other option would be using repeatWhenEmpty when you might want to poll till you get either success or failure from the publisher. When there are scenarios you don't want to wait forever to get the response, rather want to have a timeout, you could use the same method where you have own logic or libraries' method to timeout the operation.

AtomicInteger c = new AtomicInteger();
Mono<String> source = Mono.defer(() -> c.getAndIncrement() < 3 
? Mono.empty() 
: Mono.just("7"));
return source.repeatWhenEmpty(4, Flux::cache);

In the above example 4 specifies, maximum number of times you want to retry.

source.repeatWhenEmpty(exponentialBackOff(Duration.ofMillis(1), 
                Duration.ofMillis(15),
                Duration.ofSeconds(1)));

https://github.com/cloudfoundry/cf-java-client has the exponentialBackOff utils mehtod.