본문 바로가기

코틀린

Kotlin Flow #4 - onXXX 함수와 예외처리

반응형

이번에는 알아볼 것은 플로우에서 시작, 완료, 에러 등의 상태 처리 등을 하는 함수들이다.

시작할 때

flowOf(1, 2, "String", 3).onStart {
    emit("Start1!!!")
}.onStart {
    emit("Start2!!!")
}.collect { println(it) }

result : 
Start1!!!
Start2!!!
1
2
String
3

onStart 함수는 Flow 가 실행을 시작할 때 특정 데이터를 방출할 수 있도록 지원한다.

선언 위치는 사용전이면 상관없으며 여러 번 사용하면 순서대로 전부 실행된다.

비어있을 때

emptyFlow<String>().onEmpty {
    emit("is Not Empty")
}.collect { println(it) }

result :
is Not Empty

onEmpty 함수는 Flow 가 비어있을 때 특정 데이터를 방출할 수 있도록 지원한다.

 

선언 위치는 상관이 없으나 여러 번 사용하면 처음에 사용한 함수만 적용이 된다.

( 만약, 첫 번째 onEmpty에서 어떤 데이터도 방출하지 않으면 두 번째 onEmpty의 로직은 실행이 된다. )

emptyFlow<String>()
    .onEach { if (it !is String) throw IllegalArgumentException() }
    .withIndex()
    .onEmpty {
        emit(IndexedValue(0,"is Not Empty"))
    }
    .collect {
        println(it)
    }

예제처럼 타입이 변할 수 있는 withIndex, map, tranform 등의 뒤에

onEmpty 를 선언하면 방출해야 하는 타입이 잘 확인해야 한다.

에러 발생했을 때

flowOf(1, 2, "Three", 3)
    .onEach { check(it !is String) { "It is Not Number" } }
    .catch { println(it) }
    .collect {
        println(it)
    }

result : 
1
2
java.lang.IllegalStateException: It is Not Number

flowOf(1, 2, "Three", 3)
    .catch { println(it) }
    .onEach { check(it !is String) { "It is Not Number" } }
    .collect {
        println(it)
    }

result : 
1
2
FATAL EXCEPTION: DefaultDispatcher-worker-1
    Process: com.onetwothree.flowsample, PID: 19986
    java.lang.IllegalStateException: It is Not Number

catch 함수는 try / catch와 동일한 역할로

예외 발생 시 해당 예외처리를 위하여 사용할 수 있다.

 

인자로는 throwable 이 들어오며 예외처리 후 Flow는 알아서 종료된다.

catch 함수는 upStream 만 집어낼 수 있기에 사용 위치가 매우 중요하다.

 

첫 번째 예제는 "에러 발생 시점 이후"에 catch 를 사용하여 예외 처리가 가능했고,

두 번째 예제는 "에러 발생 시점 이전"에 사용하여 예외 처리를 못하고 에러가 발생하는 것을 확인할 수 있다.

완료되었을 때

flowOf(1, 2, "String", 3).onEach {
    check(it !is String) { "It is String" }
}.onCompletion {
    println(it)
}.collect {
    println(it)
}

result : 
1
2
FATAL EXCEPTION: DefaultDispatcher-worker-2
    Process: com.onetwothree.flowsample, PID: 21013
    java.lang.IllegalStateException: It is String

onCompletion 함수는 try / finnaly와 동일한 역할로

Flow 가 완료되었을 때의 동작을 지정할 수 있다.

 

정상적인 완료와 에러로 인한 완료 두 가지 케이스 모두 해당 함수를 실행시킨다.

인자로는 Throwable 이 들어오며, null 이면 정상종료 또는 catch 를 통해 이미 예외 처리된 경우이고,
null 이 아니면 에러로 인한 종료이다.

 

onCompletion 함수는 여러 번 사용할 수 있으며, 작성한 순서대로 동작한다.

인자도 동일한 것이 반복적으로 들어온다.

재시도가 필요할 때

flowOf(1, 2, "Three", 3)
    .onEach { if (it is String) throw IllegalArgumentException() }
    .retry(2) {
        it is IllegalArgumentException
    }
    .catch { println(it) }
    .collect {
        println(it)
    }

result :
1
2
1
2
1
2

FATAL EXCEPTION: DefaultDispatcher-worker-1
    Process: com.onetwothree.flowsample, PID: 16765
    java.lang.IllegalArgumentException

retry 는 에러로 인해 Flow 가 제대로 완료되지 않았을 때 동작한다.

에러가 날 때마다 Flow의 방출을 처음부터 다시 시작한다.

 

생성자를 통해서 재시도 횟수를 지정할 수 있으며,

횟수를 다 채우고도 에러가 발생한다면 그때 크래시가 발생한다.

( 횟수를 0 보다 작게 지정하면 에러가 발생한다. )

 

인자로는 throwable 이 넘어오고 boolean 값을 반환해야 한다.

반환 값이 true 인 경우에만 재시도를 진행한다.

flowOf(1, 2, "Three", 3)
    .onEach { if (it is String) throw IllegalArgumentException() }
    .catch { println(it) }
    .retry(2) {
        it is IllegalArgumentException
    }
    .collect {
        println(it)
    }

result :
1
2
java.lang.IllegalArgumentException

위의 retry 예제와 동일한 코드이다.

다른 점은 catch 를 추가한 것이다.

 

retry 는 에러를 받아야만 재시도를 진행하는 함수이다.
그런데 예제에선 catch 가 먼저 에러를 처리해버려서 retry 가 실행되지 않는다.

 

그러니 재시도와 예외 처리를 같이 진행하려면 retry 뒤에 catch 를 사용하거나,

위 코드의 catch 에서 새로운 에러를 던져서 retry 가 동작하도록 해야 한다.

flowOf(1, 2, "Three", 3)
    .onEach { if (it is String) throw IllegalArgumentException() }
    .retryWhen { cause, attempt ->
        cause is IllegalArgumentException && attempt < 2
    }
    .collect {
        println(it)
    }

result :
1
2
1
2
1
2

FATAL EXCEPTION: DefaultDispatcher-worker-1
    Process: com.onetwothree.flowsample, PID: 16881
    java.lang.IllegalArgumentException

retryWhenretry 와 동일한 기능을 한다.

 

대신 생성자에서 재시도 횟수를 지정하지 않고, 인자에서 현재 시도 횟수가 들어온다.

역시나 true를 리턴하면 재시도를 진행한다.

반응형

'코틀린' 카테고리의 다른 글

이펙티브 코틀린 - 음..?  (1) 2022.03.29
Kotlin Channel - 코루틴간 데이터 통신  (0) 2020.07.30
Kotlin Flow #3 - Zip And Combine  (0) 2020.07.10
Kotlin Flow #2 - 연산자들  (0) 2020.07.08
Kotlin Flow #1 - 기본 사용법  (1) 2020.07.08