0Pricing
Kotlin Academy · Lekcja

Propagacja wyjątków w async/await

Poznaj sposób propagowania wyjątków z async oraz dowiedz się, kiedy używać try-await.

Propagacja wyjątków w async/await to bezpłatna lekcja Kotlin Academy na CoddyKit. To lekcja 3 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.

async przechowuje wyjątki

W przeciwieństwie do launch, async przechowuje wyjątek w zwróconym obiekcie Deferred. Jest on zgłaszany dopiero po wywołaniu await().

import kotlinx.coroutines.*
fun main() = runBlocking {
    val deferred = async {
        throw RuntimeException("async error")
    }
    try {
        deferred.await()  // exception rethrown here
    } catch (e: RuntimeException) {
        println("Caught: ${e.message}")
    }
}

Wyjątek bez await()

Jeśli nigdy nie wywołają Państwo await(), wyjątek zostanie po cichu odrzucony przy zwykłym nadrzędnym obiekcie Job. Należy preferować supervisorScope + zawsze oczekiwać na wynik.

import kotlinx.coroutines.*
fun main() = runBlocking {
    // Exception stored in deferred, never retrieved:
    val d = async { throw RuntimeException("lost exception") }
    delay(100)  // d has failed — exception never surfaced
    println("d.isCancelled: ${d.isCancelled}")
}

async w ramach coroutineScope

W ramach zwykłego coroutineScope, jeśli korutyna potomna async zgłosi wyjątek, który zostanie przekazany dalej (nie zostanie przechwycony), anuluje zakres i całe rodzeństwo.

import kotlinx.coroutines.*
fun main() = runBlocking {
    try {
        coroutineScope {
            val a = async { "A" }
            val b = async { throw RuntimeException("B failed") }
            println(a.await())
            println(b.await())  // propagates, cancels scope
        }
    } catch (e: RuntimeException) {
        println("Scope failed: ${e.message}")
    }
}

async w ramach supervisorScope

W ramach supervisorScope wyjątki z async NIE propagują się do rodzeństwa. Każde await()` należy opakować osobno.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        val a = async { "A" }
        val b = async { throw RuntimeException("B failed") }
        println(a.await())
        try { println(b.await()) }
        catch (e: RuntimeException) { println("b failed: ${e.message}") }
    }
}

awaitAll dla wielu korutyn async

awaitAll(d1, d2, d3) oczekuje na wszystkie obiekty odroczone. Jeśli którykolwiek zakończy się niepowodzeniem, natychmiast zgłasza wyjątek i anuluje pozostałe.

import kotlinx.coroutines.*
fun main() = runBlocking {
    try {
        val results = awaitAll(
            async { "result1" },
            async { throw RuntimeException("task 2 failed") },
            async { "result3" }
        )
        println(results)
    } catch (e: Exception) {
        println("awaitAll failed: ${e.message}")
    }
}

runCatching z async

Należy opakować każde await() w runCatching, aby zebrać wyniki i błędy bez przerywania operacji po pierwszym niepowodzeniu.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        val deferreds = listOf(
            async { "A" },
            async { throw RuntimeException("B") },
            async { "C" }
        )
        val results = deferreds.map { runCatching { it.await() } }
        results.forEach { println(it) }
    }
}

Zadania równoległe z gromadzeniem błędów

Należy zebrać wszystkie niepowodzenia z równoległych zadań async i zgłosić je razem zamiast kończyć działanie po pierwszym błędzie.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val tasks = listOf("a", "b", "c")
    val results = supervisorScope {
        tasks.map { t ->
            async {
                if (t == "b") throw RuntimeException("b failed")
                t.uppercase()
            }
        }.map { runCatching { it.await() } }
    }
    val errors = results.filter { it.isFailure }
    val successes = results.mapNotNull { it.getOrNull() }
    println("Success: $successes, Errors: ${errors.size}")
}

Zachowanie typu wyjątku

Wyjątki zgłaszane wewnątrz async zachowują swój typ. Należy przechwytywać konkretny typ wyjątku przy await(), aby precyzyjnie obsługiwać błędy.

import kotlinx.coroutines.*
class NetworkException(msg: String) : RuntimeException(msg)
fun main() = runBlocking {
    val d = async { throw NetworkException("timeout") }
    try {
        d.await()
    } catch (e: NetworkException) {
        println("Network error: ${e.message}")
    } catch (e: Exception) {
        println("Other error: ${e.message}")
    }
}

Deferred.getCompleted() do sprawdzania bez zawieszania

Po wykonaniu join() należy użyć getCompleted(), aby synchronicznie pobrać wynik. Metoda zgłasza wyjątek, jeśli obiekt odroczony zakończył się niepowodzeniem.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val d = async { 42 }
    d.join()  // wait without caring about result
    try {
        val result = d.getCompleted()  // synchronous — no suspend
        println("Result: $result")
    } catch (e: Exception) {
        println("Failed: ${e.message}")
    }
}

Współbieżność strukturalna i async

W ramach współbieżności strukturalnej obiekt odroczony async jest zawsze powiązany z nadrzędnym zakresem. Gdy zakres zostanie anulowany, obiekt odroczony również zostanie anulowany — nie ma osieroconych zadań.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val scope = CoroutineScope(SupervisorJob())
    val d = scope.async {
        delay(1000)
        "result"
    }
    scope.cancel()  // cancels d too
    try { d.await() }
    catch (e: CancellationException) { println("Cancelled as expected") }
}

Dobra praktyka: zawsze wywołuj await

Należy zawsze wywoływać await() dla każdego obiektu odroczonego async, nawet jeśli wynik nie jest potrzebny. Umożliwia to wykrycie wyjątków i zapobiega cichym niepowodzeniom.

import kotlinx.coroutines.*
fun main() = runBlocking {
    supervisorScope {
        val d = async {
            // do background work
            "done"
        }
        // Always await:
        val result = runCatching { d.await() }
        println(result)
    }
}

Szybkie sprawdzenie

Kiedy ujawnia się wyjątek z bloku async { throw ... }?

Podsumowanie

async przechowuje wyjątki w obiekcie Deferred; ujawniają się one przy await(). W ramach coroutineScope propagacja anuluje rodzeństwo. W ramach supervisorScope każde wywołanie await musi samodzielnie obsłużyć własne niepowodzenie. Należy zawsze oczekiwać na każdy obiekt odroczony.

Często zadawane pytania

Czy lekcja „Propagacja wyjątków w async/await” jest bezpłatna?

Tak — pełny tekst „Propagacja wyjątków w async/await” 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 „Propagacja wyjątków w async/await”?

Poznaj sposób propagowania wyjątków z async oraz dowiedz się, kiedy używać try-await. Ć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 3 z 4.

Ile czasu zajmuje lekcja „Propagacja wyjątków w async/await”?

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. SupervisorJob a Job: izolowanie błędów
  2. CoroutineExceptionHandler: globalny handler nieprzechwyconych wyjątków
  3. Propagacja wyjątków w async/await
  4. Projektowanie odpornych architektur korutyn
← Powrót do Kotlin Academy