플로우에는 여러가지 생명주기 함수가 있다. (중간 함수)
하나씩 알아보자.
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 |