Search code examples
javareactive-programmingapache-commons-httpclientreactor

Is Mono.toFuture() blocking?


From the Official Documentation of Mono#block() it is said that:

Subscribe to this Mono and block indefinitely until a next signal is received. Returns that value, or null if the Mono completes empty. In case the Mono errors, the original exception is thrown (wrapped in a RuntimeException if it was a checked exception).

So it is sure that block() method is blocking and it will not execute the next line untill block() resolved.

But my confusion is while I was using toFuture() expecting it will be non-blocking but it is behaving exactly like block method. And in the Documentation of Mono#toFuture() it is stated:

Transform this Mono into a CompletableFuture completing on onNext or onComplete and failing on onError.

Mono#toFuture()

Not much clear. Nowhere in this doc said Mono#toFuture() is blocking.

  1. Please confirm me if toFuture() method blocking or non-blocking?
  2. Also If it is non-blocking then, which thread will responsible to execute the code inside CompletableFuture?

Update: added code snippet

using Mono.block() method:

    long time = System.currentTimeMillis();
    String block = Mono.fromCallable(() -> {
        logger.debug("inside in fromCallable() block()");
        //Upstream httpcall with apache httpClient().
        // which takes atleast 1sec to complete.
        return "Http response as string";
    }).block();
    logger.info("total time needed {}", (System.currentTimeMillis()-time));

    return CompletableFuture.completedFuture(block);

Using Mono.ToFuture() method:

    long time = System.currentTimeMillis();
    CompletableFuture<String> toFuture = Mono.fromCallable(() -> {
        logger.debug("inside in fromCallable() block()");
        //Upstream httpcall with apache httpClient().
        // which takes atleast 1sec to complete.
        return "Http response as string";
    }).toFuture();
    logger.info("total time needed {}", (System.currentTimeMillis()-time));
    return toFuture;

these two code snippets behaves exactly same.


Solution

  • TL;DR Mono.toFuture() is not blocking but Mono.toFuture().get() is blocking. block() is technically the same as toFuture().get() and both are blocking.

    Mono.toFuture() just transforms Mono into a CompletableFuture by subscribing to it and resolving immediately. But it doesn't mean that you can access result (in your case String) of the corresponding Mono after this. CompletableFuture is still async and you can use methods like thenApply(), thenCompose(), thenCombine(), ... to continue async processing.

    CompletableFuture<Double> result = getUserDetail(userId)
        .toFuture()
        .thenCompose(user -> getCreditRating(user));
    

    where getUserDetail is defined as

    Mono<User> getUserDetail(String userId);
    

    Mono.toFuture is useful when you need to combine different async APIs. For example, AWS Java v2 API is async but based on CompletableFuture but we can combine APIs using Mono.toFuture or Mono.fromFuture.