SupervisorJob frente a Job: aislamiento de fallos
Use SupervisorJob para evitar que el fallo de una hija cancele a sus hermanas.
SupervisorJob frente a Job: aislamiento de fallos es una lección gratuita de Kotlin Academy en CoddyKit. Esta es la lección 1 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.
Propagación predeterminada de errores de Job
Con un Job normal, el fallo de un hijo cancela al padre, que a su vez cancela a todos los hermanos. Un solo fallo derriba toda la jerarquía.
import kotlinx.coroutines.*
fun main() = runBlocking {
try {
coroutineScope {
launch { delay(50); throw RuntimeException("child 1 failed") }
launch { delay(1000); println("child 2 — never prints") }
}
} catch (e: RuntimeException) {
println("Caught: ${e.message}")
}
}Descripción general de SupervisorJob
SupervisorJob cambia esta regla: el fallo de un hijo NO cancela a los hermanos ni al padre. Cada hijo falla de forma independiente.
import kotlinx.coroutines.*
fun main() = runBlocking {
val supervisor = SupervisorJob()
val scope = CoroutineScope(coroutineContext + supervisor)
scope.launch { throw RuntimeException("child 1 failed") }
scope.launch { delay(100); println("child 2 still runs") }
delay(200)
supervisor.cancel()
}Constructor supervisorScope
supervisorScope { } crea un ámbito cuyo padre es un SupervisorJob. Es la forma idiomática de ejecutar hijos independientes.
import kotlinx.coroutines.*
fun main() = runBlocking {
supervisorScope {
val job1 = launch {
throw RuntimeException("job1 failed")
}
val job2 = launch {
delay(100)
println("job2 succeeded")
}
job1.join() // wait for job1 (it failed)
job2.join() // job2 is unaffected
}
}Capturar fallos de los hijos
Con supervisorScope, cada hijo gestiona su propia excepción. Las excepciones no gestionadas de los hijos deben capturarse individualmente en cada hijo.
import kotlinx.coroutines.*
fun main() = runBlocking {
supervisorScope {
val result = async {
delay(50)
throw RuntimeException("async failed")
}
try {
result.await()
} catch (e: RuntimeException) {
println("Caught from async: ${e.message}")
}
println("Scope continues")
}
}Job frente a SupervisorJob al crear un ámbito
Pase SupervisorJob() al crear un CoroutineScope para un servicio o ViewModel que deba sobrevivir a los fallos individuales de sus hijos.
import kotlinx.coroutines.*
class MyService {
private val scope = CoroutineScope(Dispatchers.Default + SupervisorJob())
fun startTask(name: String) {
scope.launch {
if (name == "fail") throw RuntimeException("$name failed")
println("$name done")
}
}
fun stop() = scope.cancel()
}
fun main() = runBlocking {
val svc = MyService()
svc.startTask("fail")
svc.startTask("ok")
delay(100)
svc.stop()
}viewModelScope utiliza SupervisorJob
El viewModelScope de Android utiliza un SupervisorJob. Un lanzamiento fallido no cancela las demás corrutinas del ViewModel.
import kotlinx.coroutines.*
// Android ViewModel internals:
// val viewModelScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
// Each launch is independent — one failure doesn't crash the ViewModel
fun main() = runBlocking {
println("viewModelScope = SupervisorJob + Main")
}Descomposición paralela con supervisorScope
Use supervisorScope + async para tareas paralelas en las que algunas puedan fallar sin afectar a las demás.
import kotlinx.coroutines.*
fun main() = runBlocking {
val results = supervisorScope {
val a = async { delay(50); "result-A" }
val b = async { throw RuntimeException("B failed") }
val c = async { delay(30); "result-C" }
listOf(
runCatching { a.await() },
runCatching { b.await() },
runCatching { c.await() }
)
}
results.forEach { println(it) }
}Cuándo usar un Job normal
Use un Job normal (coroutineScope) cuando todos los hijos deban completarse correctamente; un solo fallo debe cancelar el grupo. Es como una transacción: todo o nada.
import kotlinx.coroutines.*
fun main() = runBlocking {
try {
coroutineScope { // regular Job
val a = async { delay(50); "A" }
val b = async { throw RuntimeException("B failed") }
println(a.await())
println(b.await()) // throws — cancels a too
}
} catch (e: RuntimeException) {
println("Transaction failed: ${e.message}")
}
}Propagación de excepciones en supervisorScope
En supervisorScope, una excepción se propaga al scope solo si sale del nivel superior del hijo; es decir, si no hay try-catch en el hijo. El padre no la recibe.
import kotlinx.coroutines.*
fun main() = runBlocking {
supervisorScope {
// Exception caught inside child — scope unaffected
launch {
try { throw RuntimeException("handled") }
catch (e: Exception) { println("Child caught: ${e.message}") }
}
delay(100)
println("Scope survived")
}
}CoroutineExceptionHandler con SupervisorJob
Instale un CoroutineExceptionHandler en un scope supervisor para registrar o gestionar los fallos no capturados de los hijos sin que el scope se bloquee.
import kotlinx.coroutines.*
fun main() = runBlocking {
val handler = CoroutineExceptionHandler { _, e ->
println("Uncaught: ${e.message}")
}
val scope = CoroutineScope(SupervisorJob() + handler)
scope.launch { throw RuntimeException("unhandled failure") }
scope.launch { delay(100); println("still running") }
delay(200)
scope.cancel()
}Resumen del aislamiento de fallos
Job: un hijo falla → todos fallan. SupervisorJob: los hijos fallan de forma independiente. Use supervisor para servicios y ViewModels; use un Job normal para grupos transaccionales.
import kotlinx.coroutines.*
// Decision table:
// coroutineScope { } -> Job: all-or-nothing
// supervisorScope { } -> SupervisorJob: independent failures
// CoroutineScope(SupervisorJob()) -> long-lived service
fun main() = runBlocking { println("Pick the right job for the right scope") }Comprobación rápida
¿Qué scope evita que el fallo de un hijo cancele a sus hermanos?
Resumen
Job: el fallo se propaga a los hermanos. SupervisorJob/supervisorScope: los fallos se aíslan por hijo. Use supervisorScope para tareas paralelas independientes y coroutineScope normal para grupos transaccionales.
Preguntas frecuentes
¿La lección «SupervisorJob frente a Job: aislamiento de fallos» es gratis?
Sí — el texto completo de «SupervisorJob frente a Job: aislamiento de fallos» 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 «SupervisorJob frente a Job: aislamiento de fallos»?
Use SupervisorJob para evitar que el fallo de una hija cancele a sus hermanas. 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 1 de 4.
¿Cuánto tiempo toma la lección «SupervisorJob frente a Job: aislamiento de fallos»?
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
- SupervisorJob frente a Job: aislamiento de fallos
- CoroutineExceptionHandler: gestor global de excepciones no capturadas
- Propagación de excepciones en async/await
- Diseñar arquitecturas de corrutinas resilientes