Mono switchIfEmpty() is always called

和自甴很熟 提交于 2019-12-03 02:32:11

It's because switchIfEmpty accepts Mono "by value". Meaning that even before you subscribe to your mono, this alternative mono's evaluation is already triggered.

Imagine a method like this:

Mono<String> asyncAlternative() {
    return Mono.fromFuture(CompletableFuture.supplyAsync(() -> {
        System.out.println("Hi there");
        return "Alternative";
    }));
}

If you define your code like this:

Mono<String> result = Mono.just("Some payload").switchIfEmpty(asyncAlternative());

It'll always trigger alternative no matter what during stream construction. To address this you can defer evaluation of a second mono by using Mono.defer

Mono<String> result = Mono.just("Some payload")
        .switchIfEmpty(Mono.defer(() -> asyncAlternative()));

This way it will only print "Hi there" when alternative is requested

UPD:

Elaborating a little on my answer. The problem you're facing is not related to Reactor but to Java language itself and how it resolves method parameters. Let's examine the code from the first example I provided.

Mono<String> result = Mono.just("Some payload").switchIfEmpty(asyncAlternative());

We can rewrite this into:

Mono<String> firstMono = Mono.just("Some payload");
Mono<String> alternativeMono = asyncAlternative();
Mono<String> result = firstMono.switchIfEmpty(alternativeMono);

These two code snippets are semantically equivalent. We can continue unwrapping them to see where the problem lies:

Mono<String> firstMono = Mono.just("Some payload");
CompletableFuture<String> alternativePromise = CompletableFuture.supplyAsync(() -> {
        System.out.println("Hi there");
        return "Alternative";
    }); // future computation already tiggered
Mono<String> alternativeMono = Mono.fromFuture(alternativePromise);
Mono<String> result = firstMono.switchIfEmpty(alternativeMono);

As you can see future computation was already triggered at the point when we start composing our Mono types. To prevent unwanted computations we can wrap our future into a defered evaluation:

Mono<String> result = Mono.just("Some payload")
        .switchIfEmpty(Mono.defer(() -> asyncAlternative()));

Which will unwrap into

Mono<String> firstMono = Mono.just("Some payload");
Mono<String> alternativeMono = Mono.defer(() -> Mono.fromFuture(CompletableFuture.supplyAsync(() -> {
        System.out.println("Hi there");
        return "Alternative";
    }))); // future computation defered
Mono<String> result = firstMono.switchIfEmpty(alternativeMono);

In second example the future is trapped in a lazy supplier and is scheduled for execution only when it will be requested.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!