πŸ• Tempo di lettura: 2 minuti

Nell'ambito di WebFlux, Γ¨ comune gestire catene di operazioni asincrone. Tuttavia, puΓ² capitare di imbattersi in output particolari come Mono<Mono<Void>>. Un esempio? Supponiamo di avere un metodo startCar(keys) che, date le chiavi di un'auto (sampleCar), l'avvia (cambiandone lo stato da STOPPED a RUNNING) e restituisca un Mono<Void>. Per applicare realmente questo cambiamento di stato, Γ¨ necessario effettuare una sottoscrizione esplicita o implicita del Mono.

πŸ“Œ Esempio con subscribe() esplicito

final CarState actualState = Mono.just(carKeys)
    .doOnNext(keys -> sampleCar.startCar(keys).subscribe())
    .map(keys -> sampleCar.getState())
    .block();
assertEquals(CarState.RUNNING, actualState);

⚠️ Attenzione: se avessimo scritto .doOnNext(sampleCar::startCar) senza invocare subscribe(), l'auto non sarebbe effettivamente stata avviata, il Mono<Void> sarebbe rimasto inattivo, l'azione non eseguita e l'assertEquals sarebbe fallito.

πŸ“Œ Esempio con sottoscrizione implicita

Alternativamente, Γ¨ possibile effettuare la sottoscrizione in maniera implicita "appiattendo" il Mono<Mono<Void>> in un Mono<Void> con flatMap, che gestisce implicitamente la sottoscrizione, e utilizzando l'operatore then() per concatenare un'operazione successiva al Mono precedente.

final CarState actual = Mono.just(carKeys)
    .flatMap(sampleCar::startCar)
    .then(Mono.defer(() -> Mono.just(sampleCar.getState())))
    .block();

πŸ“‘ Visualizzare l'evoluzione dei tipi nella catena

Vediamo nel dettaglio come i tipi si trasformano lungo la pipeline reattiva β€” utile per capire perchΓ© serve la sottoscrizione e quando si ottiene un 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();
}

πŸ” Considerazioni aggiuntive

Mono.defer(() -> Mono.just(...)) Γ¨ utilizzato per differire la creazione del Mono fino al momento della sottoscrizione, assicurando la corretta esecuzione di logica dipendente dallo stato corrente.

Alla prossima pillola! β˜•