Kotlin Academy · Lekcja

catch i onCompletion: obsługa błędów w Flow

Przechwytuj wyjątki z upstreamu i reaguj na zdarzenia zakończenia przepływu.

Lekcja 2 z 413 kroki

catch i onCompletion: obsługa błędów w Flow to bezpłatna lekcja Kotlin Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Kotlin Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Kotlin Academy zawiera 4 lekcji w sumie.

Podstawy wyjątków w Flow

Wyjątki w strumieniu Flow kończą jego działanie. Bez obsługi wyjątek propaguje się do odbiorcy, a strumień zostaje zakończony.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    try {
        flow {
            emit(1)
            throw RuntimeException("stream error")
            emit(2)
        }.collect { println(it) }
    } catch (e: Exception) {
        println("Caught: ${e.message}")
    }
}

Operator catch

catch obsługuje wyjątki z nadrzędnego strumienia i może emitować wartości zastępcze albo ponownie zgłosić wyjątek. Nie przechwytuje wyjątków zgłoszonych przez samego odbiorcę.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flow {
        emit(1)
        throw RuntimeException("oops")
    }.catch { e ->
        println("Caught: ${e.message}")
        emit(-1)  // fallback value
    }.collect { println(it) } // 1, then -1
}

catch obsługuje tylko nadrzędny strumień

catch przechwytuje wyłącznie wyjątki z konstruktora strumienia Flow i operatorów nadrzędnych — nie wyjątki z lambdy collect.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    try {
        flowOf(1, 2, 3)
            .catch { e -> println("upstream: ${e.message}") }
            .collect {
                if (it == 2) throw RuntimeException("collector error") // not caught by catch
                println(it)
            }
    } catch (e: Exception) {
        println("Collector exception: ${e.message}")
    }
}

Ponowne zgłaszanie wyjątków w catch

Możesz przeanalizować wyjątek i wybiórczo ponownie zgłosić te wyjątki, których nie możesz obsłużyć.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    try {
        flow {
            emit(1)
            throw IllegalStateException("state error")
        }.catch { e ->
            if (e is RuntimeException) { emit(-1); return@catch }
            throw e  // rethrow unknown exceptions
        }.collect { println(it) }
    } catch (e: Exception) {
        println("Rethrown: ${e.message}")
    }
}

Operator onCompletion

onCompletion wykonuje się po zakończeniu strumienia Flow — normalnym lub spowodowanym wyjątkiem. Jego parametr cause ma wartość inną niż null w przypadku błędu.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flowOf(1, 2, 3)
        .onCompletion { cause ->
            if (cause == null) println("Completed normally")
            else println("Failed: ${cause.message}")
        }
        .collect { println(it) }
}

onCompletion w przypadku błędu

onCompletion wykonuje się nawet wtedy, gdy wystąpi wyjątek w nadrzędnym strumieniu. Otrzymuje wyjątek jako cause, ale go nie wycisza — wyjątek nadal się propaguje.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    try {
        flow {
            emit(1)
            throw RuntimeException("error")
        }.onCompletion { cause ->
            println("onCompletion cause=${cause?.message}")
        }.collect { println(it) }
    } catch (e: Exception) {
        println("outer catch: ${e.message}")
    }
}

Łączenie catch i onCompletion

Użyj catch, aby obsłużyć błąd i wyemitować wartości zastępcze, a następnie onCompletion, aby niezależnie od wyniku zarejestrować zdarzenie lub zwolnić zasoby.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flow {
        emit("ok")
        throw RuntimeException("network error")
    }.catch { e ->
        emit("fallback") // recover
    }.onCompletion {
        println("Stream done — release resources")
    }.collect { println(it) }
}

Operator retry

retry(n) { ... } ponownie subskrybuje nadrzędny strumień po wystąpieniu wyjątku, maksymalnie n razy.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
var attempt = 0
fun unstableFlow() = flow {
    attempt++
    if (attempt < 3) throw RuntimeException("attempt $attempt failed")
    emit("success on attempt $attempt")
}
fun main() = runBlocking {
    unstableFlow()
        .retry(3) { e -> println("retrying: ${e.message}"); true }
        .collect { println(it) }
}

retryWhen dla niestandardowej logiki

retryWhen udostępnia wyjątek i liczbę prób, dzięki czemu możesz dodać opóźnienie między próbami lub określić, które wyjątki powodują ponowienie.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    var n = 0
    flow {
        if (n++ < 2) throw IOException("IO error")
        emit("done")
    }.retryWhen { cause, attempt ->
        cause is IOException && attempt < 3
    }.collect { println(it) }
}

Operator onStart

onStart wykonuje się przed pierwszą emisją — przydaje się do wyświetlania stanu ładowania lub inicjowania zasobów przed nadejściem danych.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flowOf("data")
        .onStart { emit("Loading...") }
        .onCompletion { emit("Done") }
        .collect { println(it) }
}

Obsługa błędów w rzeczywistych wywołaniach API

Opakuj przepływy repozytorium za pomocą catch, aby mapować wyjątki na błędy domenowe, oraz onCompletion, aby ukrywać wskaźniki ładowania.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
sealed class State { object Loading : State(); data class Data(val v: String) : State(); data class Error(val msg: String) : State() }
fun apiFlow(): Flow<String> = flow { delay(50); emit("response") }
fun uiFlow(): Flow<State> = apiFlow()
    .map { State.Data(it) as State }
    .onStart { emit(State.Loading) }
    .catch { e -> emit(State.Error(e.message ?: "unknown")) }
fun main() = runBlocking { uiFlow().collect { println(it) } }

Szybkie sprawdzenie

Jakie jest główne ograniczenie operatora catch?

Podsumowanie

catch obsługuje wyjątki z przepływu źródłowego i może emitować wartości zastępcze. onCompletion wykonuje się zawsze (po pomyślnym zakończeniu lub błędzie) w celu sprzątania albo rejestrowania zdarzeń. Należy łączyć je z retry/retryWhen, aby tworzyć odporne potoki przepływów.

Bezpłatny start

Ucz się Kotlin dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
51
Lekcje
203

Często zadawane pytania

Czy lekcja „catch i onCompletion: obsługa błędów w Flow” jest bezpłatna?

Tak — pełny tekst „catch i onCompletion: obsługa błędów w Flow” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Kotlin Academy, przejdź na CoddyKit PRO. Kurs Kotlin Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „catch i onCompletion: obsługa błędów w Flow”?

Przechwytuj wyjątki z upstreamu i reaguj na zdarzenia zakończenia przepływu. Ćwiczysz Kotlin Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Kotlin Academy?

Nie wymagamy żadnego doświadczenia. Kotlin Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „catch i onCompletion: obsługa błędów w Flow”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Kotlin Academy?

Tak. Każda lekcja Kotlin Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Operatory Flow: map, filter, transform i take
  2. catch i onCompletion: obsługa błędów w Flow
  3. combine i zip: łączenie wielu Flow
  4. flowOn i buffer: kontekst i backpressure
← Powrót do Kotlin Academy