I'm trying to understand the behaviour of .then(Mono(x))
in spring webflux.
I have the following code:
fun storeIfValid(x): Mono<Long> =
doSomeChecksThatMightFail().then(repo.save(x))
Reading the documentation of then
lets me believe, that the repo.save(x)
block is only executed when the onComplete
signal is published from the previous Mono. In case the first Mono fails, I would then expect no interaction with my repository.
I know I could "fix" it by using the following:
fun storeIfValid(x): Mono<Long> =
doSomeChecksThatMightFail().flatMap{ repo.save(x) }
or also the following works:
fun storeIfValid(x): Mono<Long> =
doSomeChecksThatMightFail().then(Mono.defer{repo.save(x)})
but I'm struggling to fully understand why. It seems that the Mono in the then
-block is already executed before the initial Mono completes?
Maybe someone can shed a bit of light on this topic.. all explanations I found so far (stackoverflow, chatgpt and documentation) were a bit lacklustre.
Arguments passed to a method call are always evaluated before the method is called. repo.save(x)
must be evaluated (i.e. invoked) before then
is called, so whatever then
does will have no effect on when repo.save(x)
is called.
What then
does in this case, is to create a new Mono
that plays the Mono
returned by repo.save(x)
, after the Mono
returned by doSomeChecksThatMightFail()
has completed.
In other words, x
is saved immediately, but the Mono
returned by storeIfValid
will only emit the saved instance (returned by repo.save(x)
) after doSomeChecksThatMightFail()
has completed.
In the flatMap
and defer
cases, repo.save(x)
is in a lambda expression. When a lambda expression is evaluated, it just creates an object representing that lambda expression. It is only after the lambda has been invoked, will the code in the lambda expression run.
For flatMap
, the lambda is called when doSomeChecksThatMightFail()
emits a value. For defer
, the lambda is called when the Mono
is played, i.e. after doSomeChecksThatMightFail
completes, because of .then(...)
.
Note that the flatMap
and defer
approaches are slightly different. flatMap
will not emit a value or run the lambda if doSomeChecksThatMightFail
completes without emitting a value.