У меня есть сценарий, в котором мне нужно найти метод, возвращающий строковый ответ из внешнего API. У меня есть две возможности для этого ответа: дать мне действительный ответ (с параметром 1 или параметром 2) или, если оба ответа недействительны, вернуть в цепочку окончательного пустого издателя.
Mono<String> checkResponse(String parameter)
на самом деле у меня есть
checkResponse(stringArg1)
.switchIfEmpty(checkResponse(stringArg2))
.flatMapMany ...
.flatMap ...
метод
public Mono<String> checkResponse(String s)
return webClient.post()
.uri(URI)
.body(BodyInserters.fromValue(s))
.retrieve()
.bodyToMono(String.class)
Но switchIfEmpty всегда выполняется.
С уважением,
Я делаю mapNotNull и проверяю, является ли ответ строкового узла пустым или нет. Если не пустой, верните строку, иначе верните ноль.
Я имею в виду, что когда результат не равен нулю, метод вызывается/подписывается дважды и не игнорирует (пропускает) метод switchIfEmpty.
Вы уверены, что он на самом деле излучает дважды?
Важно понимать два аспекта Project Reactor:
Этот код:
checkResponse(stringArg1)
.switchIfEmpty(checkResponse(stringArg2));
соберет Mono
s для обоих вызовов checkResponse
.
По сути, checkResponse
-метод вызывается дважды, однако только Mono
, возвращенный из первого checkResponse
-вызова, будет подписан до тех пор, пока он выдает элемент.
Вы можете проверить это поведение следующим образом:
checkResponse(stringArg1)
.doOnSubscribe(s -> System.out.println("First checkResponse subscription"))
.switchIfEmpty(checkResponse(stringArg2)
.doOnSubscribe(s -> System.out.println("Second checkResponse subscription"))
);
Что очень типично для реактивного кода, так это то, что код верхнего уровня в методе, который возвращает Mono
/Flux
, обычно выполняется во время сборки, в то время как все лямбда-выражения, переданные их различным операторам, таким как map
/flatMap
/concatMap
/и т. д., выполняются во время подписки.
Проиллюстрировать:
public Mono<String> getName(int id) {
// Assembly time
System.out.println("This executes at assembly time");
return userRepo.get(id)
.map(user -> {
// Subscription time
System.out.println("This executes at subscription time");
return user.name;
});
}
Если сборка Mono
может быть дорогой, а на нее никогда не будет подписки, как в вашем случае здесь, вы можете отложить сборку до времени подписки, используя Mono.defer:
checkResponse(stringArg1)
.switchIfEmpty(Mono.defer(() -> checkResponse(stringArg2)));
На самом деле есть разница между временем сборки и временем подписки.
Время сборки — это когда вы создаете свой пайплайн, выстраивая реактивную цепочку.
Время подписки — это когда запускается выполнение и начинают поступать данные. Вы должны рассмотреть возможность использования обратных вызовов и лямбда-выражений, поскольку они лениво оцениваются.
Итак, ваш метод checkResponse()
вызывается дважды во время сборки, потому что это не лямбда, а обычный метод. И он возвращает моно
Вы можете использовать Mono.defer(() -> checkResponse())
и отложить выполнение и сборку внутреннего моно, пока не оформите подписку.
что вы подразумеваете под "всегда выполнять"? И в том, и в другом случае, когда тело есть, и в случае, когда тела нет? Вы проверили это? А если есть, то как на самом деле?