Kotlin Coroutines - Flow completion
플로우의 수집은 정상적으로 종료되든 예외가 발생되어서 중지되든 언젠가 종료가 된다.
따라서 우리는 플로우의 종료 시점에 후처리를 진행해야할 필요가 있다.
1. 명령형(Imperative) finally block
플로우를 try-catch로 감쌀 수 있듯이 finally 블록도 추가할 수 있다.
즉, 플로우가 종료된 후 실행할 동작을 finally 블록에 정의할 수 있다는 뜻이다.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun simple(): Flow<Int> = (1..3).asFlow()
fun main() = runBlocking<Unit> {
try {
simple().collect { value -> println(value) }
} finally {
println("Done")
}
}
출력 결과
1 | 1 |
플로우가 종료된 이후 “Done”이 출력된 것을 볼 수 있다.
확실하지만 매우 전통적인 방법이라 할 수 있겠다.
2. Declarative handling
onCompletion 연산자를 선언하여 플로우의 종료 후 처리 동작을 정의할 수 있다.
1의 예제와 동일한 결과를 출력하도록 onCompletion 연산자를 적용하면 아래와 같다.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun simple(): Flow<Int> = (1..3).asFlow()
fun main() = runBlocking<Unit> {
simple()
.onCompletion { println("Done") }
.collect { value -> println(value) }
}
출력 결과
1 | 1 |
3. The key advantage of onCompletion
try-catch-finally와 결과가 똑같다면 onCompletion을 사용함으로써 얻을 수 있는 것이 무엇일까?
onCompletion을 사용하면 Throwable 파라미터를 통해 플로우 수집이 정상 종료되었는지, 예외가 발생되었는지를 알 수 있다.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun simple(): Flow<Int> = flow {
emit(1)
throw RuntimeException()
}
fun main() = runBlocking<Unit> {
simple()
.onCompletion { cause -> if (cause != null) println("Flow completed exceptionally") }
.catch { cause -> println("Caught exception") }
.collect { value -> println(value) }
}
출력 결과
1 | 1 |
