πŸ• Reading time: 2 minutes

New Saturday, new library, new pill!

You know that moment when you wonder whether your Mono gets straight to work or waits for the right time – without wasting resources? No? Well, if you're not a dev or you don't use WebFlux, of course not – it'd be pretty weird otherwise, eheh! But if you're curious, here's a really, really simple example to understand the difference between Mono.just() and Mono.fromCallable().

πŸ“Œ The experiment

I compare two ways of creating a Mono to understand which one does the work first and which one puts it off until later.

class SampleTest014 {

    @Test
    void test_MonoJust_eagerEvaluation() {
        final AtomicBoolean called = new AtomicBoolean(false);
        // Mono.just eagerly evaluates its argument
        final Mono<String> actualMono = Mono.just(computeValue(called));
        assertTrue(called.get()); // computeValue has ALREADY been called
        final String actual = actualMono.block();
        assertEquals("computed", actual);
    }

    @Test
    void test_MonoFromCallable_lazyEvaluation() {
        final AtomicBoolean called = new AtomicBoolean(false);
        // Mono.fromCallable defers computation until subscription
        final Mono<String> actualMono = Mono.fromCallable(() -> computeValue(called));
        assertFalse(called.get()); // computeValue NOT YET called
        final String actual = actualMono.block();
        assertTrue(called.get()); // NOW computeValue has been called
        assertEquals("computed", actual);
    }

    // helper method to set the flag and return a fixed string
    private String computeValue(final AtomicBoolean flag) {
        flag.set(true);
        return "computed";
    }
}

πŸ“‘ Analysis of the 1st test – MonoJust_eagerEvaluation()

The method receives the value right away, so the computeValue() function is called as soon as I create the Mono. The result is ready before I even subscribe!

πŸ“‘ Analysis of the 2nd test – MonoFromCallable_lazyEvaluation()

Here, on the other hand, the function isn't called immediately, but only when the Mono is subscribed to – that is, at the moment of .block(). In practice, the work is deferred until the last possible moment.

πŸ“Œ When should you use fromCallable()?

If you have potentially blocking operations such as reading the output of an external process with a BufferedReader, fromCallable() lets you defer everything until subscription. That way you avoid kicking off the operation too early and keep the reactive flow clean!

See you at the next pill! β˜•