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
- SupervisorJob a Job: izolowanie błędów
- CoroutineExceptionHandler: globalny handler nieprzechwyconych wyjątków
- Propagacja wyjątków w async/await
- Projektowanie odpornych architektur korutyn