22

Let's say you have Mono<Integer> someIntegerSource = Mono.just(5) and you want to assign it to a variable.

Is there a difference between these code snippets?

Case 1: doOnSuccess

someIntegerSource.doOnSuccess(number -> this.myNumber = number)

Case 2: doOnNext

someIntegerSource.doOnNext(number -> this.myNumber = number)

Case 3: doOnSuccess + then (because I want to the assignment to be complete before emitting completion of the mono)

someIntegerSource.doOnSuccess(number -> this.myNumber = number).then()
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
CowZow
  • 1,275
  • 2
  • 17
  • 36

1 Answers1

59

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
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183