-1

I tried to do something with Flux streaming objects and after handling all elements do some last work and finish a Mono but it doesn't work:

 // data and id comming from a webrequest
 // myRepository is a org.springframework.data.r2dbc.repository.R2dbcRepository
 myRepository.findById(id)
    .flatMap(dbObject -> doSomethingWithDbObjectAndSave(dbObject , data))
    .then (doOnFinish(data))
    .subscribe();

Mono<DbObject> doSomethingWithDbObjectAndSave (DbObject dbo, DataObject data){
...
}
Mono<Void> doOnFinish(DataObject data){
...
}

The problem: Even I try this, the function "doOnFinish" is called before the first element pass doSomethingWithDbObjectAndSave" but I change something on the data object and would like to do this before!

The I tried to change the code:

myRepository.findById(id)
        .flatMap(dbObject -> doSomethingWithDbObjectAndSave(dbObject , data))
        .last()
        .flatMap(dbObject  -> doOnFinish(data))
        .subscribe(); 

I hoped, that I could use the last element to trigegr the onFinish function but I got "flux#last() didn't observe any onnext signal" and do not undetstand this!

Anyone any idea?

  • You are calling `.then (doOnFinish(data))` which will immediately call `doOnFinish` wtih `data` as its input; IOW: the method call is evaluated eagerly. That should probably be `.then(data -> doOnFinish(data))` or whatever the overload is called. (similar to your `flatMap` call). – knittl Sep 20 '22 at 08:25
  • 1
    @knittl you are right. He should use the `then` flavor that takes a Mono as argument, and create a deferred or callable Mono : `myFlux.then(Mono.fromCallable(() -> doOnFinish(data));` – amanin Sep 20 '22 at 14:08
  • one of the key things you need to understand switching to reactive - Assembly vs Subscription time. Check https://spring.io/blog/2019/03/06/flight-of-the-flux-1-assembly-vs-subscription for details – Alex Sep 22 '22 at 04:44

3 Answers3

0

then(methodCall(data)) will eagerly evaluate the parameter expression and thus call methodCall even before then is entered. You need a method which lazily evaluates its parameters.

I think you are looking for doOnComplete:

public final Flux<T> doOnComplete(Runnable onComplete)

Add behavior (side-effect) triggered when the Flux completes successfully.

myRepository.findById(id)
    .flatMap(dbObject -> doSomethingWithDbObjectAndSave(dbObject , data))
    .doOnComplete(() -> doOnFinish(data))
    .subscribe();
knittl
  • 246,190
  • 53
  • 318
  • 364
  • That could work, but I may have shortened my scenario a bit: Originally I would like to return myRepository.findById(id) ...... If I call doOnFinsh() onComplete as @knittl suggested, I would return the Flux Object from the second line. But I want to return the Mono from "doOnFinish", that the subscriber could controll the completion of "doOnFinish"? – Heiko Kendziorra Sep 20 '22 at 08:49
0

Wow ... hours later I found a solution. doOnComplete doesn't return the stream with the async call from doOnFinish (Mono). Now I found this:

myRepository.findById(id)
   .flatMap(dbObject -> doSomethingWithDbObjectAndSave(dbObject , data))
   .takeLast(1)
   .flatMap(dbObject  -> doOnFinish(data))
   .subscribe();

That works for me ....

0

I think flatMap is redundant here. Instead, it is possible to do:

 myRepository.findById(id)
    .flatMap(dbObject -> doSomethingWithDbObjectAndSave(dbObject , data))
    .then (Mono.deffer(()->doOnFinish(data)))
    .subscribe();
vachoh
  • 1
  • 1