Coroutines Flow 6편 –

흐름 예외 처리

흐름 캡처는 이미터 또는 연산자의 코드가 예외를 throw하는 경우 예외와 함께 완료될 수 있습니다.
예외를 처리하는 방법에는 여러 가지가 있습니다.

컬렉터 캡처 시도

수집기는 Kotlin에서 예외를 처리하는 가장 좋은 방법입니다.
시도하다/잡다 블록을 사용할 수 있습니다:

fun simple(): Flow<Int> = flow {
    for (i in 1..3) {
        println("Emitting $i")
        emit(i) // emit next value
    }
}
​
fun main() = runBlocking<Unit> {
    try {
        simple().collect { value ->         
            println(value)
            check(value <= 1) { "Collected $value" }
        }
    } catch (e: Throwable) {
        println("Caught $e")
    } 
}

완전한 코드는 여기에서 찾을 수 있습니다

이 코드 모으다 터미널 연산자 내부에서 예외를 성공적으로 포착한 후 더 이상 값이 인쇄되지 않습니다.

Emitting 1
1
Emitting 2
2
Caught java.lang.IllegalStateException: Collected 2

모든 것이 갇혀있다

이전 예제는 이미 터, 중간 또는 터미널 연산자 내의 모든 예외를 포착합니다.
예를 들어 반환된 값을 문자열에 매핑하도록 코드를 변경하면 코드에서 예외가 발생합니다.

fun simple(): Flow<String> = 
    flow {
        for (i in 1..3) {
            println("Emitting $i")
            emit(i) // emit next value
        }
    }
    .map { value ->
        check(value <= 1) { "Crashed on $value" }                 
        "string $value"
    }

fun main() = runBlocking<Unit> {
    try {
        simple().collect { value -> println(value) }
    } catch (e: Throwable) {
        println("Caught $e")
    } 
}

완전한 코드는 여기에서 찾을 수 있습니다

예외는 여전히 포착되어 더 이상 수집되지 않습니다.
*1

Emitting 1
string 1
Emitting 2
Caught java.lang.IllegalStateException: Crashed on 2

다음 내용은 독자의 이해를 돕기 위해 번역자가 추가한 내용입니다.

*하나. 예상치 못한 곳에서 예외가 발생하면 원하는 결과를 얻기 어렵기 때문에 문제가 된다.


이 문서는 공식 코루틴 문서를 번역한 것입니다.

원래의: 비동기 흐름 – 흐름 예외

원본 텍스트의 최종 편집: 2022년 9월 28일


예외 투명성

그렇다면 이미터의 코드는 예외 처리 동작을 어떻게 캡슐화할 수 있습니까?

흐름은 예외에 대해 투명해야 합니다.
시도하다/잡다 블록 내부 흐름 { … } 빌더 값을 내보내면 예외 투명성을 위반합니다.
이렇게 하면 예외를 throw하는 수집기가 항상 이전 예제와 같습니다.
시도하다/잡다예외를 잡는 데 사용할 수 있습니다.

이미 터 잡다 예외 투명성을 유지하고 예외 처리를 캡슐화하는 연산자입니다.
잡다 연산자의 본문은 예외를 구문 분석하고 포착된 예외에 따라 다른 방식으로 반응할 수 있습니다.

  • 예외는 던지다로 다시 던질 수 있습니다.
  • 잡다본문에서 방출하다값을 포함하여 예외를 방출로 전환할 수 있습니다.
  • 예외는 다른 코드에서 무시하거나 기록하거나 처리할 수 있습니다.

예를 들어 예외를 포착하는 부분에서 텍스트를 인쇄해 보겠습니다.
*하나

simple()
    .catch { e -> emit("Caught $e") } // emit on exception
    .collect { value -> println(value) }

완전한 코드는 여기에서 찾을 수 있습니다

투명한 폐쇄

예외의 투명성 존중 잡다 중간 연산자는 업스트림 예외만 포착합니다(잡다 위 연산자의 예외만 포착하고 아래 예외는 포착하지 않습니다.
만약에 모으다 { … } 내부 블록(catch 아래의 코드)이 예외를 throw하면 예외가 catch되지 않습니다.

fun simple(): Flow<Int> = flow {
    for (i in 1..3) {
        println("Emitting $i")
        emit(i)
    }
}

fun main() = runBlocking<Unit> {
    simple()
        .catch { e -> println("Caught $e") } // does not catch downstream exceptions
        .collect { value ->
            check(value <= 1) { "Collected $value" }                 
            println(value) 
        }
}

완전한 코드는 여기에서 찾을 수 있습니다

잡다 교환원이 있어도 “Caught…”라는 메시지가 뜨지 않는다.

Emitting 1
1
Emitting 2
Exception in thread "main" java.lang.IllegalStateException: Collected 2
	at ...

선언적 캐치

모으다 오퍼레이터의 몸 각각에 안쪽으로 이동 잡다 뒤에 연산자를 넣으면 잡다 연산자의 선언적 특성과 모든 예외를 처리하려는 욕구를 결합할 수 있습니다.
모으다()매개변수 없이 사용하면 흐름 모음을 생성할 수 있습니다.

simple()
    .onEach { value ->
        check(value <= 1) { "Collected $value" }                 
        println(value) 
    }
    .catch { e -> println("Caught $e") }
    .collect()

완전한 코드는 여기에서 찾을 수 있습니다

이제 “Caught…” 메시지가 명시적으로 인쇄된 것을 볼 수 있습니다.
시도하다/잡다 모든 예외는 블록을 사용하지 않고 잡을 수 있습니다.

Emitting 1
1
Emitting 2
Caught java.lang.IllegalStateException: Collected 2


다음 내용은 독자의 이해를 돕기 위해 번역자가 추가한 내용입니다.

*하나. 이 코드의 출력은 다음과 같습니다.

Emitting 1
string 1
Emitting 2
Caught java.lang.IllegalStateException: Crashed on 2

2가 발생하면 check(value <= 1)에 의해 불법 상태 예외가 발생하지만 catch 블록에서 이 예외가 포착된 후 "Caught java.lang.IllegalStateException: Crashed on 2"로 다시 발생합니다.


이 문서는 공식 코루틴 문서를 번역한 것입니다.

원래의: 비동기 흐름 – 예외 투명성

원본 텍스트의 최종 편집: 2022년 9월 28일