Read the documentation of the class Mono
and see the diagrams. The differences are not as subtle as they appear:
Mono::doOnNext
triggers when the data is emitted successfully, which means the data is available and present.
Mono::doOnSuccess
triggers when the Mono
completes successfully - result is either T
or null
, which means the processing itself successfully finished regardless the state of data and is executed although the data are not available or present but the pipeline itself succeed.
Mono::then
as the end of the methods chain returns Mono<Void>
on complete and error signals.
- Note here the payload is actively dropped, that's why there becomes
Mono<Void>
from Mono<T>
. Note the two methods above don't throw the payload away.
Follow the illustrative examples below:
Non-empty Mono
A Mono
that holds a value triggers doOnNext
when the data is emitted successfully. This can be confusing to the doOnSuccess
, but contrary to such trigger, doOnNext
is triggered when any successful value is emitted including an empty Mono
, which is still valid.
Mono.just("Hello World")
.doOnNext(i -> System.out.println("On next: " + i))
.doOnSuccess(i -> System.out.println("On success: " + i))
.doOnError(i -> System.out.println("On error: " + i))
.block();
On next: Hello World
On success: Hello World
Empty Mono
Remember, although the Mono
is empty (Mono.empty()
), it is still a valid response that triggers doOnSuccess
, however not doOnNext
, An empty Mono
can be understood as one with a valid response that doesn't represent the desired output that contains a useful value. It works on the same principle as Optional.empty()
. The Mono
is successful but has no real useful value that would trigger doOnNext
as it doesn't emit any value at all.
Mono.empty()
.doOnNext(i -> System.out.println("On next: " + i))
.doOnSuccess(i -> System.out.println("On success: " + i))
.doOnError(i -> System.out.println("On error: " + i))
.block();
Mono.just("Hello World")
.mapNotNull(s -> null)
.doOnNext(i -> System.out.println("On next: " + i))
.doOnSuccess(i -> System.out.println("On success: " + i))
.doOnError(i -> System.out.println("On error: " + i))
.block();
Both result in the same output:
On success: null
Erroneous Mono
For sake of completeness to put in contrast with an empty Mono
, the erroneous doesn't trigger either doOnNext
or doOnSuccess
but does doOnError
instead:
Mono.error(new RuntimeException("Something wrong"))
.doOnNext(i -> System.out.println("On next: " + i))
.doOnSuccess(i -> System.out.println("On success: " + i))
.doOnError(i -> System.out.println("On error: " + i))
.block();
On error: java.lang.RuntimeException: Something wrong
A side note about then
The method Mono::then
with either no argument or its overridden variant accepting Mono<V>
discards the former result and supplies a new Mono<Void
or Mono<V>
respectively.
Mono.empty()
.then()
.doOnSuccess(i -> System.out.println("On success: " + i))
.doOnError(i -> System.out.println("On error: " + i))
.block();
Mono.empty()
.then(Mono.just("Good bye"))
.doOnSuccess(i -> System.out.println("On success: " + i))
.doOnError(i -> System.out.println("On error: " + i))
.block();
Mono.just("Hello World")
.then(Mono.just("Good bye"))
.doOnSuccess(i -> System.out.println("On success: " + i))
.doOnError(i -> System.out.println("On error: " + i))
.block();
Mono.error(new RuntimeException("Something wrong"))
.then(Mono.just("Good bye"))
.doOnSuccess(i -> System.out.println("On success: " + i))
.doOnError(i -> System.out.println("On error: " + i))
.block();
Mono.error(new RuntimeException("Something wrong"))
.then(Mono.error(new RuntimeException("Something very wrong")))
.doOnSuccess(i -> System.out.println("On success: " + i))
.doOnError(i -> System.out.println("On error: " + i))
.block();
On success: null
On success: Good bye
On success: Good bye
On error: java.lang.RuntimeException: Something wrong
On error: java.lang.RuntimeException: Something wrong