Kotlin Academy · Lección

Propagación de excepciones en async/await

Comprenda cómo se propagan las excepciones de async y cuándo usar try-await.

Lección 3 de 413 pasos

Propagación de excepciones en async/await es una lección gratuita de Kotlin Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Kotlin Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Kotlin Academy incluye 4 lecciones en total.

async almacena las excepciones

A diferencia de launch, async almacena la excepción en el Deferred devuelto. Solo se vuelve a lanzar cuando se llama a 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}")
    }
}

Excepción sin await()

Si nunca llama a await(), la excepción se descarta silenciosamente cuando el padre es un Job normal. Prefiera supervisorScope + esperar siempre el resultado.

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 dentro de coroutineScope

En un coroutineScope normal, si un hijo async lanza una excepción y esta se propaga (no se captura), cancela el scope y a todos sus hermanos.

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 dentro de supervisorScope

En supervisorScope, las excepciones de async NO se propagan a los hermanos. Cada await()` debe envolverse individualmente.

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 para varios async

awaitAll(d1, d2, d3) espera a todos los Deferred. Si alguno falla, lanza una excepción inmediatamente y cancela los demás.

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 con async

Envuelva cada await() en runCatching para recopilar resultados y errores sin interrumpir el proceso al primer fallo.

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

Tareas paralelas con acumulación de errores

Recopile todos los fallos de las tareas async paralelas e infórmelos conjuntamente en lugar de fallar rápidamente.

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

Conservación del tipo de excepción

Las excepciones lanzadas dentro de async conservan su tipo. Capture el tipo de excepción específico en await() para gestionar los errores con precisión.

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() para comprobar sin suspender

Después de join(), use getCompleted() para recuperar el resultado de forma síncrona. Lanza una excepción si el deferred ha fallado.

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

Concurrencia estructurada y async

En la concurrencia estructurada, un deferred de async siempre está vinculado a su scope padre. Cuando se cancela el scope, el deferred también se cancela; no quedan tareas huérfanas.

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

Buena práctica: espere siempre el resultado

Llame siempre a await() para cada deferred de async, aunque no necesite el resultado. Así se hacen visibles las excepciones y se evitan fallos silenciosos.

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

Comprobación rápida

¿Cuándo se hace visible la excepción de un bloque async { throw ... }?

Resumen

async almacena las excepciones en Deferred; estas se hacen visibles en await(). En coroutineScope, la propagación cancela a los hermanos. En supervisorScope, cada await debe gestionar su propio fallo. Espere siempre el resultado de cada deferred.

Gratis para empezar

Aprende Kotlin con un tutor de IA — gratis

Escribe y ejecuta código real en tu navegador, obtén ayuda instantánea de un tutor de IA disponible 24/7 y continúa donde lo dejaste en la web o en la aplicación.

Cursos
51
Lecciones
203

Preguntas frecuentes

¿La lección «Propagación de excepciones en async/await» es gratis?

Sí — el texto completo de «Propagación de excepciones en async/await» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Kotlin Academy, actualiza a CoddyKit PRO. El curso de Kotlin Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Propagación de excepciones en async/await»?

Comprenda cómo se propagan las excepciones de async y cuándo usar try-await. Practicas Kotlin Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Kotlin Academy?

No se requiere experiencia previa. Kotlin Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Propagación de excepciones en async/await»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Kotlin Academy?

Sí. Cada lección de Kotlin Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. SupervisorJob frente a Job: aislamiento de fallos
  2. CoroutineExceptionHandler: gestor global de excepciones no capturadas
  3. Propagación de excepciones en async/await
  4. Diseñar arquitecturas de corrutinas resilientes
← Volver a Kotlin Academy