🎯 Fundamentos de Corrutinas
📖 Comenzando con Corrutinas
Para usar corrutinas, primero necesita conocer los constructores de corrutinas. ¡Aprendamos sobre los tres constructores más básicos!
💡 runBlocking
Uso Básico
import kotlinx.coroutines.*
fun main() = runBlocking { // ¡Inicia la corrutina!
println("Hello")
delay(1000) // Espera 1 segundo
println("World!")
}
Características
fun main() {
println("Before")
runBlocking {
delay(1000)
println("Inside coroutine")
}
println("After") // Espera hasta que la corrutina termine
}
// Before
// (espera 1 segundo)
// Inside coroutine
// After
Atención: ¡runBlocking bloquea el hilo actual! Úselo principalmente solo en pruebas o en la función main.
🚀 launch
Iniciando Tareas Asíncronas
fun main() = runBlocking {
launch {
delay(1000)
println("Task 1")
}
launch {
delay(500)
println("Task 2")
}
println("Main")
}
// Main
// (después de 500ms) Task 2
// (después de 1000ms) Task 1
Devuelve Job
fun main() = runBlocking {
val job = launch {
repeat(5) { i ->
println("Working... $i")
delay(500)
}
}
delay(1300)
println("¡Cancelando tarea!")
job.cancel() // Cancela la tarea
job.join() // Espera a que se complete la cancelación
}
Gestionando Múltiples Tareas
fun main() = runBlocking {
val jobs = List(5) { i ->
launch {
delay(1000L * i)
println("Tarea $i completada")
}
}
jobs.forEach { it.join() } // Espera todas las tareas
println("¡Todas las tareas terminadas!")
}
⚡ async
Recibiendo Resultados
fun main() = runBlocking {
val deferred = async {
delay(1000)
return@async "valor de resultado"
}
println("Trabajando...")
val result = deferred.await() // Espera el resultado
println("Valor recibido: $result")
}
launch vs async
fun main() = runBlocking {
// launch - sin resultado
launch {
delay(1000)
println("launch completado")
}
// async - devuelve resultado
val result = async {
delay(1000)
"async completado"
}
println(result.await())
}
🎯 Ejemplos Prácticos
Procesamiento Paralelo
import kotlin.system.measureTimeMillis
fun main() = runBlocking {
val time = measureTimeMillis {
val one = async { fetchData1() }
val two = async { fetchData2() }
println("Resultado: ${one.await()}, ${two.await()}")
}
println("Tiempo transcurrido: ${time}ms") // ~1000ms (ejecución paralela)
}
suspend fun fetchData1(): String {
delay(1000)
return "datos1"
}
suspend fun fetchData2(): String {
delay(1000)
return "datos2"
}
Secuencial vs Paralelo
fun main() = runBlocking {
// ❌ Ejecución secuencial (lenta)
val time1 = measureTimeMillis {
val one = fetchData1()
val two = fetchData2()
println("$one, $two")
}
println("Secuencial: ${time1}ms") // ~2000ms
// ✅ Ejecución paralela (rápida)
val time2 = measureTimeMillis {
val one = async { fetchData1() }
val two = async { fetchData2() }
println("${one.await()}, ${two.await()}")
}
println("Paralelo: ${time2}ms") // ~1000ms
}
Manejo de Excepciones
fun main() = runBlocking {
val deferred = async {
delay(1000)
throw Exception("¡Ocurrió un error!")
}
try {
deferred.await()
} catch (e: Exception) {
println("Error capturado: ${e.message}")
}
}
🔥 Patrones Útiles
Timeout
fun main() = runBlocking {
try {
withTimeout(1300) {
repeat(3) { i ->
println("Tarea $i")
delay(500)
}
}
} catch (e: TimeoutCancellationException) {
println("¡Tiempo agotado!")
}
}
Múltiples Llamadas a API
data class UserData(
val profile: String,
val posts: List<String>,
val friends: List<String>
)
suspend fun loadUserData(userId: String): UserData = coroutineScope {
val profile = async { fetchProfile(userId) }
val posts = async { fetchPosts(userId) }
val friends = async { fetchFriends(userId) }
UserData(
profile.await(),
posts.await(),
friends.await()
)
}
suspend fun fetchProfile(id: String): String {
delay(500)
return "Profile($id)"
}
suspend fun fetchPosts(id: String): List<String> {
delay(800)
return listOf("Post1", "Post2")
}
suspend fun fetchFriends(id: String): List<String> {
delay(600)
return listOf("Friend1", "Friend2")
}
Lógica de Reintento
suspend fun <T> retryIO(
times: Int = 3,
delay: Long = 1000,
block: suspend () -> T
): T {
repeat(times - 1) { attempt ->
try {
return block()
} catch (e: Exception) {
println("Intento ${attempt + 1} fallido")
delay(delay)
}
}
return block() // Último intento
}
fun main() = runBlocking {
val result = retryIO(times = 3) {
fetchDataFromAPI()
}
println(result)
}
var attempt = 0
suspend fun fetchDataFromAPI(): String {
attempt++
if (attempt < 3) {
throw Exception("Error de red")
}
return "¡Éxito!"
}
🛠️ coroutineScope
Concurrencia Estructurada
suspend fun doWork() = coroutineScope {
launch {
delay(1000)
println("Tarea 1")
}
launch {
delay(2000)
println("Tarea 2")
}
println("Todas las tareas iniciadas")
// Espera hasta que todos los hijos terminen
}
fun main() = runBlocking {
doWork()
println("¡Completado!")
}
Propagación de Excepciones
suspend fun riskyWork() = coroutineScope {
launch {
delay(500)
throw Exception("¡Error!")
}
launch {
delay(1000)
println("Este código no se ejecuta")
}
}
fun main() = runBlocking {
try {
riskyWork()
} catch (e: Exception) {
println("Error capturado: ${e.message}")
}
}
🤔 Preguntas Frecuentes
P1. ¿Cuál es la diferencia entre launch y async?
R: ¡Si devuelve resultado o no!
fun main() = runBlocking {
// launch - no necesita resultado
launch {
println("Tarea simple")
}
// async - necesita resultado
val result = async {
"valor a devolver"
}
println(result.await())
}
P2. ¿Cuál es la diferencia entre join() y await()?
R: ¡join espera la finalización, await espera el resultado!
fun main() = runBlocking {
val job = launch {
delay(1000)
}
job.join() // Solo espera la finalización
val deferred = async {
delay(1000)
"resultado"
}
val result = deferred.await() // Espera el resultado
}
P3. ¿Las corrutinas se cancelan automáticamente?
R: ¡Si el padre se cancela, los hijos también se cancelan!
fun main() = runBlocking {
val parent = launch {
val child = launch {
repeat(10) {
println("Tarea hija $it")
delay(500)
}
}
delay(1300)
println("Padre completado")
}
delay(2000)
parent.cancel() // Los hijos también se cancelan
}
🎬 Conclusión
¡Ha dominado los constructores básicos de corrutinas!
Resumen clave:
✅ runBlocking - Para pruebas/función main
✅ launch - Tareas sin resultado necesario
✅ async - Recibir resultados
✅ coroutineScope - Concurrencia estructurada
✅ Mejora del rendimiento con procesamiento paralelo
Próximo paso: ¡Aprenda en profundidad sobre las funciones de suspensión en funciones suspend!