#
Mono.just() vs Mono.defer() vs Mono.fromSupplier() vs Mono.create()
This tutorial explains the creation of Monos with Mono.just(), Mono.defer(), Mono.fromSupplier(), Mono.create() and the differences between them.
Info
Mono and Flux are both implementations of the Publisher interface. Mono represents a stream of zero or one element, whereas a Flux represents a stream of zero to many elements.
Mono.just()
: emits an element (or nothing) when the 1st subscriber subscribe to it. We have an eager generation
(the next subscribers will receive the same value).
Mono.defer()
: emits an element (or nothing) when the 1st subscriber subscribe to it. We have a lazy generation.
(the next subscribers could receive the values, and that depends on the implementation). The implementation acts
as a wrapper of Mono.just().
Mono.fromSupplier()
: emits an element (or nothing) when the 1st subscriber subscribe to it. We have a lazy generation.
(the next subscribers could receive new values, and that depends on the implementation). We need to provide the
element (which is not a Mono) which will be emitted (generally we have a method call).
Mono.create()
: emits an element (or nothing) when the 1st subscriber subscribe to it. We have a lazy generation.
(the next subscribers could receive new values, and that depends on the implementation). This implementation helps us
to create more complex code, by letting us throw an errors or emitting the stream value.
Take a look at the following example created with Spring Boot 3.0, maven in Kotlin:
package com.example.demo1
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import reactor.core.publisher.Mono
import reactor.core.publisher.MonoSink
import java.io.IOException
import java.time.LocalDateTime
@SpringBootApplication
class DemoMonoApplication
suspend fun main(args: Array<String>) {
runApplication<Demo1Application>(*args)
fun successfulRemoteCall(): LocalDateTime {
Thread.sleep(1000)
println("Getting the response ...")
return LocalDateTime.now()
}
// Mono.just()
fun monoJust(): Mono<LocalDateTime> = Mono.just(successfulRemoteCall())
val monoJust = monoJust()
println("")
println(">>> Mono.just() <<<")
monoJust.subscribe({print("monoJust/ 1st subscription: "); println(it)})
monoJust.subscribe({print("monoJust/ 2nd subscription: "); println(it)})
// Mono.defer()
fun monoDefer(): Mono<LocalDateTime> = Mono.defer { monoJust() }
val monoDefer = monoDefer()
println("")
println(">>> Mono.defer() <<<")
monoDefer.subscribe({print("monoDefer/ 1st subscription: "); println(it)})
monoDefer.subscribe({print("monoDefer/ 2nd subscription: "); println(it)})
// Mono.fromSupplier()
fun monoFromSupplier(): Mono<LocalDateTime> = Mono.fromSupplier { successfulRemoteCall() }
val monoFromSupplier = monoFromSupplier()
println("")
println(">>> Mono.fromSupplier() <<<")
monoFromSupplier.subscribe({print("monoFromSupplier/ 1st subscription: "); println(it)})
monoFromSupplier.subscribe({print("monoFromSupplier/ 2nd subscription: "); println(it)})
// Mono.create()
println("")
println(">>> Mono.create() 1 <<<")
fun getAnyString1(): String {
return "My String"
}
fun getAnyString2(): String {
throw IllegalArgumentException("An error as occurred for no reason.");
}
val monoCreate1 = Mono.create<String?> { sink: MonoSink<String?> ->
//
try {
println("SOME CODE could be here ...")
var var1 = getAnyString1();
sink.success(var1)
} catch (e: Exception) {
sink.error(IOException("File path is invalid"))
}
}
monoCreate1.subscribe({print("monoCreate1/ 1st subscription: "); println(it)})
monoCreate1.subscribe({print("monoCreate1/ 2nd subscription: "); println(it)})
println("")
println(">>> Mono.create() 2 <<<")
val monoCreate2 = Mono.create<String?> { sink: MonoSink<String?> ->
//
try {
println("SOME CODE could be here ...")
var var1 = getAnyString2();
sink.success(var1)
} catch (e: Exception) {
// We throw an exception
sink.error(IOException("File path is invalid"))
}
}
monoCreate2.subscribe({print("monoCreate1/ 1st subscription: "); println(it)})
}
When we run this code we receive the following output:
Getting the response ...
>>> Nano.just() <<<
monoJust/ 1st subscription: 2023-09-23T09:26:19.457888700
monoJust/ 2nd subscription: 2023-09-23T09:26:19.457888700
>>> Nano.defer() <<<
Getting the response ...
monoDefer/ 1st subscription: 2023-09-23T09:26:20.475227600
Getting the response ...
monoDefer/ 2nd subscription: 2023-09-23T09:26:21.488765
>>> Nano.fromSupplier() <<<
Getting the response ...
monoFromSupplier/ 1st subscription: 2023-09-23T09:26:22.492847600
Getting the response ...
monoFromSupplier/ 2nd subscription: 2023-09-23T09:26:23.510062300
>>> Nano.create() 1 <<<
SOME CODE could be here ...
monoCreate1/ 1st subscription: My String
SOME CODE could be here ...
monoCreate1/ 2nd subscription: My String
>>> Nano.create() 2 <<<
SOME CODE could be here ...
2023-09-23T09:26:23.514+03:00 ERROR 8748 --- [ main] reactor.core.publisher.Operators : Operator called default onErrorDropped
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.io.IOException: File path is invalid
Info
"java.io.IOException" exception is just for explanation, and it doesn't make any sense in my code.