🕐 Reading time: 2 minutes
When working with WebFlux, it's common to handle chains of asynchronous operations. Sometimes, though, you might run into unusual outputs like Mono<Mono<Void>>. An example? Let's assume we have a startCar(keys) method that, given a car's keys (sampleCar), starts it (changing its state from STOPPED to RUNNING) and returns a Mono<Void>. To actually apply this state change, you need to subscribe to the Mono, either explicitly or implicitly.
📌 Example with explicit subscribe()
final CarState actualState = Mono.just(carKeys)
.doOnNext(keys -> sampleCar.startCar(keys).subscribe())
.map(keys -> sampleCar.getState())
.block();
assertEquals(CarState.RUNNING, actualState);
⚠️ Watch out: if we had written .doOnNext(sampleCar::startCar) without calling subscribe(), the car wouldn't actually have been started, the Mono<Void> would have stayed inactive, the action wouldn't have run, and the assertEquals would have failed.
📌 Example with implicit subscription
Alternatively, you can subscribe implicitly by "flattening" the Mono<Mono<Void>> into a Mono<Void> with flatMap, which handles the subscription implicitly, and then using the then() operator to chain a follow-up operation onto the previous Mono.
final CarState actual = Mono.just(carKeys)
.flatMap(sampleCar::startCar)
.then(Mono.defer(() -> Mono.just(sampleCar.getState())))
.block();
📑 Visualising how the types evolve along the chain
Let's look in detail at how the types transform along the reactive pipeline – handy for understanding why the subscription is needed and when you end up with a Mono<Mono<Void>>:
@Test
void test_subscribe() {
final String licensePlate = "BMT 216A";
final Car sampleCar = new Car(licensePlate);
Mono.just(carKeys) //Mono<String>
.map(sampleCar::startCar) //Mono<Mono<Void>>
.map(Mono::subscribe) //Mono<Disposable>
.map(disposable -> sampleCar) //Mono<Car>
.map(Car::getState) //Mono<CarState>
.doOnNext(actual -> assertEquals(CarState.RUNNING, actual))
.block();
}
🔍 Extra considerations
Mono.defer(() -> Mono.just(...)) is used to defer the creation of the Mono until subscription time, ensuring that logic depending on the current state runs correctly.
See you at the next pill! ☕