플로우 생명주기 함수
- 플로우는 요청이 한쪽 방향으로 흐르고, 요청에 의해 생성된 값이 다른 방향으로 흐르는 파이프 형태입니다.
- 플로우가 완료되거나 예외가 발생하면 값이나 예외 같은 특정 이벤트를 감지할 수 있습니다.
- 이 때 onEach, onStart, onCompletion, onEmpty와 같은 catch 메서드를 사용할 수 있습니다.
onEach
- onEach는 중단 함수로, 순서대로 값을 하나씩 받아 처리할 수 있습니다.
suspend fun main() {
flowOf(1,2,3,4)
.onEach {
delay(1000)
}
.collect{
println(it)
}
}
onEmpty
- 예기치 않은 이벤트가 발생하면 플로우는 값을 내보내기 전에 종료됩니다.
- onEmpty는 기본값을 내보내기 위한 목적으로 사용될 수 있습니다.
suspend fun main() = coroutineScope {
// 값을 내보내지 않는 플로우
flow<List<Int>> { delay(1000) }
// 아무 값을 내보내지 않았으므로, onEmpty 호출
.onEmpty { emit(emptyList()) }
.collect { println(it) }
}
onStart
- 플로우가 시작되는 경우 호출되는 리스너입니다.
- emit을 활용하여 초기값을 수집할 수 있습니다.
suspend fun main(){
flowOf(1,2)
.onStart { println("Start") }
.collect{ println(it) }
flowOf(1,2)
.onStart { emit(0) }
.collect{ println(it) }
}
onCompletion
- 플로우 빌더가 끝났을 때, 즉 마지막 원소를 전송한 경우 호출되는 리스너입니다.
suspend fun main(){
flowOf(1,2)
.onEach { delay(1000) }
.onCompletion { println("Completed") }
.collect{ println(it) }
}
catch
- 플로우를 만들거나 처리하는 도중에 발생하는 에러를 제어할 수 있습니다.
- onEach와 함께 사용하는 경우, 예외가 발생한 케이스는 onEach에서 호출되지 않습니다.
class MyError : Throwable("error")
val flow = flow{
emit(1)
emit(2)
throw MyError()
}
suspend fun main(){
flow.onEach { println(it) } // onEach는 예외에 잡히지 않음
.catch { println(it) } // 예외 출력
.collect{ println(it) }
}
flowOn
- 플로우 연산과 람다식은 모두 중단 함수로, 컨텍스트를 필요로 합니다.
- 이는 구조화 된 동시성과 관련이 있으며, 부모 자식 관계를 유지합니다.
- 플로우의 함수들은 collect가 호출되는 곳의 컨텍스트를 활용합니다.
- currentcoroutineContext() 호출 시 현재 호출 컨텍스트 반환
fun userFlow(): Flow<String> = flow {
repeat(2){
val ctx= currentCoroutineContext()
val name = ctx[CoroutineName]?.name
emit("User$it in $name")
}
}
suspend fun main() {
val users = userFlow()
withContext(CoroutineName("Name1")){
users.collect{
println(it)
}
}
withContext(CoroutineName("Name2")){
users.collect {
println(it)
}
}
}
- 위 경우에 flowOn을 활용할 수 있습니다.
- 이를 활용하면 컨텍스트를 변경할 수 있습니다.
- flowOn은 플로우에서 윗부분에 있는 함수에서만 동작한다는 특징이 있습니다.
suspend fun main() {
withContext(CoroutineName("Name1")) {
users
.flowOn(CoroutineName("Name2"))
.onEach { println(it) }
.flowOn(CoroutineName("Name3"))
.onEach { println(it) }
.collect {
println("collect $it")
}
}
}
launchIn
- 유일한 인자로 스코프를 받아 collect를 새로운 코루틴에서 시작하도록 돕는 빌더입니다.
- collect는 플로우가 완료될 때까지 코루틴을 중단하는 중단 연산입니다.
- launchIn 빌더로 collect를 래핑하면 플로우를 다른 코루틴에서 처리할 수 있습니다.
// 별도의 코루틴에서 플로우를 시작합니다.
suspend fun main(): Unit = coroutineScope {
flowOf("user1","user2")
.onStart{ println("User Info")}
.onEach { println(it) }
.launchIn(this)
}