Programming
Reactor3's SwitchIfEmpty with lazy evaluation..
모종
2023. 3. 10. 23:23
반응형
Reactor3의 switchIfEmpty 함수를 쓰면서 의도치 않은 버그를 만들게 되면서 관련 이슈 포스팅을 간단하게 하려고 합니다.
버그 코드
@Test
fun `버그 코드`() {
Mono.just("monny.tistory.com")
.doOnNext { println("doOnNext -> $it") }
.switchIfEmpty(handle())
.map {
println("map -> $it")
it.uppercase()
}
.subscribe { println("result -> $it") }
}
private fun handle(): Mono<String> {
println("handle")
return Mono.just("handle")
}
먼저 버그코드입니다. Mono에 이미 element를 가지고 있는 상태로 실행하기 때문에 switchIfEmpty는 당연히 실행이 안될 것이라고 생각했었습니다. 실제 위 코드를 실행했을 때 결과는 아래와 같습니다.

뜬금없이 handle이 실행되었다는 것을 알 수 있었습니다. 그것도 순서가 굉장히 수상합니다. 어디서 많이 본듯한 현상인데 eager evaluation 현상입니다.
Lazy Evaluation으로 바꾸자

위의 코드에 lazy evaluation을 적용하기 위해서 supplier를 제공해 주면 됩니다. reactor에 fromSupplier나 defer 등 Supplier를 이용할 수 있는 함수가 있는데 지금은 defer가 적절해 보입니다.
@Test
fun `lazy evaluation 적용`() {
Mono.just("monny.tistory.com")
.doOnNext { println("doOnNext -> $it") }
.switchIfEmpty(Mono.defer { handle() })
.map {
println("map -> $it")
it.uppercase()
}
.subscribe { println("result -> $it") }
}

이제 handle이 바로 호출이 되지 않고 실제 실행시점에 호출이 되는 것으로 바뀌었습니다. 따라서 handle 메서드는 실행이 되지 않았습니다. 아래는 element를 주지 않고 실행시켰을 때 결과입니다. handle이 제 순서에 실행되고 있습니다.
@Test
fun `lazy evaluation 적용2`() {
Mono.empty<String>()
.doOnNext { println("doOnNext -> $it") }
.switchIfEmpty(Mono.defer { handle() })
.map {
println("map -> $it")
it.uppercase()
}
.subscribe { println("result -> $it") }
}

반응형