본문 바로가기
도서/코틀린 코루틴

플로우 생명주기 함수

by 안스 인민군 2024. 3. 24.

플로우에는 여러가지 생명주기 함수가 있다. (중간 함수)

하나씩 알아보자.

onEach

onEach 함수는 값을 하나씩 받는다. 따라서 onEach 함수에 delay를 넣으면 값을 받아올 때마다 delay 함수가 실행이 된다.

suspend fun main() {
    flowOf(1,2,3).onEach { delay(1000) }.collect(::println)
} 
// 
1초 후
1
1초 후
2
1초 후
3

onStart

onStart 함수는 이름 그대로 collect 최종 연산이 실행될 때 호출되는 리스너 이다.

첫 번째 원소를 요청했을 때 호출되는 것을 확인할 수 있다.

suspend fun main() {
    flowOf(1, 2, 3).onStart { println("start") }.collect(::println)
}
//
start
1
2
3

onStart 에서도 값을 방출할 수 있다. 이 때 원소들은 onStart 아래로 호출하게 된다.

suspend fun main() {
    flowOf(1, 2, 3).onStart { emit(0) }.collect(::println)
}
//
0
1
2
3

onCompletion

onCompletion함수는 이름 그대로 플로우가 완료되었을 때 호출되는 리스너 이다.

suspend fun main() {
    flowOf(1, 2, 3).onCompletion { println("finish") }.collect(::println)
}
1
2
3
finish

실제 Android 개발에서 사용할 때 progress 를 그리거나 가리기 위해 onStart 와 onCompletion 함수를 사용할 수 있다.

onEmpty

onEmpty 함수는 플로우에 예기치 못한 상황으로 값을 방출하기전에 플로우가 완료될 때 호출되는 리스너 이다.

suspend fun main() {
    emptyFlow<Int>().onEmpty { println("empty value") }.collect(::println)
}
//
empty value

catch

플로우에서 예외를 처리하기 위해 사용하는 중간함수 이다.

suspend fun main() {
    flowOf(1, 2, 3).onCompletion {
        throw Error("error 발생")
    }.catch {
        println(it.message)
    }.collect(::println)
}
//
1
2
3
error 발생

catch 메서드는 예외를 잡아 전파하는 걸 멈춘다. 이전 단계는 멈춘 상태지만 catch 에는 값을 여전히 방출할 수 있다.

또한 catch 함수를 사용할 때 주의해야하는 점이, 윗 부분에서 발생 된 예외만 잡을 수 있다.

suspend fun main() {
    flowOf(1, 2, 3).catch {
        println(it.message)
    }.onStart {
        throw Error("error 발생")
    }.onCompletion {
        println("finish")
    }.collect(::println)
}
//
finish
Exception in thread "main" java.lang.Error: error 발생

따라서 실제로 사용할 때 catch 함수를 중간 함수중 맨 상위에서 호출해야 한다.

잡히지 않는 예외

플로우에서 잡히지 않은 예외는 플로우를 즉시 취소하고 collect는 예외를 다시 던진다.

중간함수(coroutineScope) 가 처리하는 방식과 동일하다.

Flow 바깥에서는 try - catch 로 예외를 잡을 수 있다.

suspend fun main() {
    val flow = flow {
        emit("start flow block")
        throw Error("error 발생")
    }
    try {
        flow.collect(::println)
    } catch (e: Exception) {
        println(e.message)
    }
}
//
start flow block
Exception in thread "main" java.lang.Error: error 발생

플로우 내부에서 발생된 예외는 try - catch 를 사용으로 예외를 잡을 수가 없다.

collect 에서 예외가 발생하면 예외를 잡지못하므로, collect 연산을 옮기며 해결할 수 있다.

suspend fun main() {
    val flow = flow {
        emit(1)
        emit(2)

    }
    flow.onStart {
        println("start")
    }.onEach {
        println(it)
    }.onEach {
        throw Error("error 발생")
    }.catch {
        println(it.message)
    }.collect()
}
//
start
1
error 발생

기존 collect 에서 수행될 코드를 onEach 로 옮겨 예외를 잡을 수 있다.

flowOn

flowOn 함수를 사용해 CoroutineContext 를 변경할 수 있다.

withContext 와 동일한 역할을 수행한다.

suspend fun main() {
    val flow = flow {
        emit(1)
    }

    withContext(CoroutineName("test1")) {
        flow.onEach {
            println(currentCoroutineContext()[CoroutineName])
        }.flowOn(
            CoroutineName("test2")
        ).collect {
            println(currentCoroutineContext()[CoroutineName])
        }
    }
}
//
CoroutineName(test2)
CoroutineName(test1)

launchIn

launchIn 함수로 collect 를 매핑하면 다른 코루틴에서 해당 플로우를 처리할 수 있다.

public fun <T> Flow<T>.launchIn(scope: CoroutineScope): Job = scope.launch {
    collect() // tail-call
}

suspend fun main() {
    coroutineScope {
        flowOf(1, 2).onStart {
            println(0)
        }.onEach {
            println(it)
        }.launchIn(this)
    }
}
//
0
1
2

'도서 > 코틀린 코루틴' 카테고리의 다른 글

공유플로우와 상태플로우  (0) 2024.03.24
플로우 만들기  (0) 2024.03.24
플로우의 실제 구현  (0) 2024.03.24
핫 데이터 소스와 콜드 데이터 소스  (0) 2024.03.24
플로우란 무엇인가?  (0) 2024.03.24