Я пытаюсь понять поведение .then(Mono(x))
в Spring WebFlux.
У меня есть следующий код:
fun storeIfValid(x): Mono<Long> =
doSomeChecksThatMightFail().then(repo.save(x))
Чтение документации then
позволяет мне поверить, что блок repo.save(x)
выполняется только тогда, когда публикуется сигнал onComplete
из предыдущего Mono. В случае сбоя первого Mono я не ожидаю никакого взаимодействия с моим репозиторием.
Я знаю, что могу «исправить» это, используя следующее:
fun storeIfValid(x): Mono<Long> =
doSomeChecksThatMightFail().flatMap{ repo.save(x) }
или также следующие работы:
fun storeIfValid(x): Mono<Long> =
doSomeChecksThatMightFail().then(Mono.defer{repo.save(x)})
но я изо всех сил пытаюсь полностью понять, почему. Кажется, что Mono в блоке then
уже выполняется до завершения первоначального Mono?
Может быть, кто-то сможет пролить немного света на эту тему... все объяснения, которые я нашел до сих пор (stackoverflow,
Аргументы, передаваемые при вызове метода, всегда оцениваются до вызова метода. repo.save(x)
должен быть оценен (т. е. вызван) до вызова then
, поэтому все, что делает then
, не повлияет на то, когда будет вызван repo.save(x)
.
В этом случае then
создает новый Mono
, который воспроизводит Mono
, возвращенный repo.save(x)
, после завершения Mono
, возвращенного doSomeChecksThatMightFail()
.
Другими словами, x
сохраняется немедленно, но Mono
, возвращаемый storeIfValid
, будет генерировать сохраненный экземпляр (возвращенный repo.save(x)
) только после завершения doSomeChecksThatMightFail()
.
В случаях flatMap
и defer
repo.save(x)
находится в лямбда-выражении. Когда вычисляется лямбда-выражение, оно просто создает объект, представляющий это лямбда-выражение. Только после вызова лямбда-выражения запускается код лямбда-выражения.
Для flatMap
лямбда вызывается, когда doSomeChecksThatMightFail()
выдает значение. Для defer
лямбда вызывается при воспроизведении Mono
, т. е. после завершения doSomeChecksThatMightFail
из-за .then(...)
.
Обратите внимание, что подходы flatMap
и defer
немного отличаются. flatMap
не будет выдавать значение или запускать лямбду, если doSomeChecksThatMightFail
завершится без выдачи значения.
Большое спасибо! Я думаю, что самый важный вывод — это первый абзац. Я поставлю
.then(Mono.defer { })
, так как считаю, что отсутствие выполнения на картах в пустотах часто упускается из виду и может быстро привести к ошибкам.