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 |