这是因为 switchIfEmpty “按值”接受 Mono。这意味着,即使在您订阅单声道之前,该替代单声道的评估就已经被触发。
想象一下这样的方法:
Mono<String> asyncAlternative() {
return Mono.fromFuture(CompletableFuture.supplyAsync(() -> {
System.out.println("Hi there");
return "Alternative";
}));
}
如果您像这样定义代码:
Mono<String> result = Mono.just("Some payload").switchIfEmpty(asyncAlternative());
无论在流构建过程中如何,它总是会触发替代方案。为了解决这个问题,您可以使用以下方法推迟对第二个单声道的评估Mono.defer
Mono<String> result = Mono.just("Some payload")
.switchIfEmpty(Mono.defer(() -> asyncAlternative()));
这样,当需要替代方案时,它只会打印“Hi There”
UPD:
对我的答案进行一些阐述。您面临的问题与Reactor无关,而是与Java语言本身以及它如何解析方法参数有关。让我们检查一下我提供的第一个示例中的代码。
Mono<String> result = Mono.just("Some payload").switchIfEmpty(asyncAlternative());
我们可以将其重写为:
Mono<String> firstMono = Mono.just("Some payload");
Mono<String> alternativeMono = asyncAlternative();
Mono<String> result = firstMono.switchIfEmpty(alternativeMono);
这两个代码片段在语义上是等效的。我们可以继续拆开看看问题出在哪里:
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);
正如您所看到的,当我们开始编写我们的代码时,未来的计算已经被触发Mono
类型。为了防止不必要的计算,我们可以将您的未来纳入延迟评估中:
Mono<String> result = Mono.just("Some payload")
.switchIfEmpty(Mono.defer(() -> asyncAlternative()));
这将展开成
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);
在第二个示例中,Future 被困在一个懒惰的供应商中,并且仅在被请求时才安排执行。
最新更新:2022:
自从一段时间以来,项目 Reactor 附带了一个替代 API,用于包装急切计算的 future,这会导致相同的结果 - 将急切的计算捕获在一个懒惰的供应商中:
Mono<String> result = Mono.just("Some payload")
.switchIfEmpty(Mono.fromCompletionStage(() -> alternativePromise()));