Kotlin Academy · Oppitunti

async/await-poikkeusten eteneminen

Ymmärtäkää, miten async-funktioiden poikkeukset etenevät ja milloin try-await-rakennetta käytetään.

Oppitunti 3/413 vaihetta

async/await-poikkeusten eteneminen on ilmainen Kotlin Academy-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu Kotlin Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Kotlin Academy-kurssilla on yhteensä 4 oppituntia.

async tallentaa poikkeukset

Toisin kuin launch, async tallentaa poikkeuksen palautettuun Deferred-objektiin. Poikkeus heitetään uudelleen vasta, kun kutsutte 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}")
    }
}

Poikkeus ilman await()-kutsua

Jos ette koskaan kutsu await()-funktiota, poikkeus hylätään hiljaisesti tavallisen Job-vanhemman yhteydessä. Suosikaa rakennetta supervisorScope + käyttäkää aina await-kutsua.

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 coroutineScopen alla

Tavallisen coroutineScope-rakenteen alla async-lapsen heittämä poikkeus, joka välittyy eteenpäin eikä jää kiinni, peruuttaa scopen ja kaikki sisarukset.

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 supervisorScopen alla

supervisorScope-rakenteen alla async-poikkeukset EIVÄT välity sisaruksille. Jokainen await()` on käsiteltävä erikseen.

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 useille async-operaatioille

awaitAll(d1, d2, d3) odottaa kaikkia deferred-objekteja. Jos jokin epäonnistuu, se heittää poikkeuksen heti ja peruuttaa muut.

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 async-operaatioiden kanssa

Ympäröikää jokainen await()-kutsu runCatching-rakenteella, jotta voitte kerätä tulokset ja virheet ilman suorituksen keskeytymistä ensimmäiseen virheeseen.

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) }
    }
}

Rinnakkaiset tehtävät ja virheiden kerääminen

Kerätkää kaikki rinnakkaisten async-tehtävien epäonnistumiset ja ilmoittakaa niistä yhdessä sen sijaan, että suoritus epäonnistuisi heti ensimmäiseen virheeseen.

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}")
}

Poikkeustyypin säilyminen

async-rakenteen sisällä heitetyt poikkeukset säilyttävät tyyppinsä. Ottakaa await()-kutsun yhteydessä kiinni täsmällisestä poikkeustyypistä, jotta voitte käsitellä virheen tarkasti.

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() ei-keskeyttävään tarkistukseen

Käyttäkää join()-kutsun jälkeen getCompleted()-funktiota tuloksen hakemiseen synkronisesti. Se heittää poikkeuksen, jos deferred epäonnistui.

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}")
    }
}

Rakenteinen samanaikaisuus ja async

Rakenteisessa samanaikaisuudessa async-deferred on aina linkitetty vanhempaan scopeensa. Kun scope peruutetaan, myös deferred peruutetaan — orpoja tehtäviä ei synny.

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") }
}

Paras käytäntö: odottakaa aina

Kutsukaa aina await()-funktiota jokaiselle async-deferredille, vaikka ette tarvitsisi tulosta. Näin poikkeukset tulevat näkyviin ja hiljaiset epäonnistumiset estetään.

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

Pikatarkistus

Milloin async { throw ... } -lohkon poikkeus tulee näkyviin?

Kertaus

async tallentaa poikkeukset Deferred-objektiin, ja ne tulevat näkyviin await()-kutsun yhteydessä. coroutineScope-rakenteen alla välittyminen peruuttaa sisarukset. supervisorScope-rakenteen alla jokaisen await-kutsun on käsiteltävä oma epäonnistumisensa. Odottakaa aina jokaista deferred-objektia.

Aloita maksutta

Opi Kotlin tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
51
Oppitunnit
203

Usein kysytyt kysymykset

Onko oppitunti ”async/await-poikkeusten eteneminen” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa Kotlin Academy-oppimispolun 3 oppituntia, myös oppitunnin “async/await-poikkeusten eteneminen”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Kotlin Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”async/await-poikkeusten eteneminen”?

Ymmärtäkää, miten async-funktioiden poikkeukset etenevät ja milloin try-await-rakennetta käytetään. Harjoittelet Kotlin Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Kotlin Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Kotlin Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.

Kuinka kauan ”async/await-poikkeusten eteneminen”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Kotlin Academy-oppitunnilla?

Kyllä. Jokainen Kotlin Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. SupervisorJob vs Job: virheiden eristäminen
  2. CoroutineExceptionHandler: maailmanlaajuinen käsittelemätön poikkeusten käsittelijä
  3. async/await-poikkeusten eteneminen
  4. Vikasietoisten korutiiniarkkitehtuurien suunnittelu
← Takaisin: Kotlin Academy