Search code examples
project-reactorflatmap

Are Reactor operators like flatMap() that runs on same main thread any more efficient than regular blocking code?


I understand that a lot of reactor operators like flatMap() run on the same thread on which the onNext method was invoked. What I am trying to understand is if such a method is any more efficient/performant non-blocking than a regular blocking call in say a for loop. Sorry if it's a noob question but I can't seem to grasp it. Is the power of reactive realized only when we use Schedulers that jump threads (e.g. Schedulers.parallel()) and so on?

e.g. if I had a function like the following

List<Integer> integers = Arrays.asList(5,6,7,8,9,10);
    Flux.fromIterable(integers)
            .flatMap(this::makeAReactiveMethodCall)
            .subscribe(r -> log.info(Thread.currentThread().getName()));

Logs look something like this - notice all the threads are the same "main" one.

01:25:40.641 INFO ReactiveTest - main
01:25:40.642 INFO ReactiveTest - main
01:25:40.642 INFO ReactiveTest - main
01:25:40.642 INFO ReactiveTest - main
01:25:40.642 INFO ReactiveTest - main
01:25:40.642 INFO ReactiveTest - main

All invocations happen on the same main thread. How is this code more efficient than making all the call in a for loop with blocking semantics? Say the following code?

integers.forEach(i -> this.makeAReactiveMethodCall(i).block());

Solution

  • Assuming each makeAReactiveMethodCall does some I/O and takes 1 second to complete. Using the flatMap operator your calls will be made asynchronously. This means that the main thread will make all 6 calls without waiting for the I/O operation to complete(non-blocking), instead, it will process some other work and will be notified when a call is completed. In the case of WebClient and Project Reactor, this is achieved by using the Netty event loop to queue/dispatch/process events.

    In the traditional, blocking way(eg. RestTemplate), it would take 6 seconds to make all 6 calls synchronously. Of course, you could use ExecutorService API to make it asynchronously, but in that case, you would need 6 threads because calls would be blocking. One of the advantages of the reactive model is that number of threads is limited, thus, resources are not wasted in multiple thread creation.