플로우 생명주기 함수

플로우 생명주기 함수

  • 플로우는 요청이 한쪽 방향으로 흐르고, 요청에 의해 생성된 값이 다른 방향으로 흐르는 파이프 형태입니다.
  • 플로우가 완료되거나 예외가 발생하면 값이나 예외 같은 특정 이벤트를 감지할 수 있습니다.
  • 이 때 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)
}