Saltar al contenido principal

🚀 ¿Por qué Coroutines?

📖 ¿Qué son las Coroutines?

Las Coroutines (Corutinas) son una característica fundamental de Kotlin que hace que la programación asíncrona sea fácil e intuitiva. ¡Puede escribir código como si fuera síncrono, escapando del complejo infierno de callbacks!

💡 Problemas de la programación asíncrona tradicional

Infierno de callbacks

// ❌ Método de callbacks (difícil de leer y mantener)
fun fetchUser(userId: String, callback: (User?) -> Unit) {
fetchFromNetwork(userId) { result ->
if (result != null) {
fetchProfile(result.id) { profile ->
if (profile != null) {
fetchPosts(profile.id) { posts ->
callback(User(result, profile, posts))
}
} else {
callback(null)
}
}
} else {
callback(null)
}
}
}

El costo de los hilos

// ❌ Los hilos son costosos
fun downloadFiles() {
repeat(1000) {
Thread {
// Cada hilo consume mucha memoria
downloadFile("file_$it.txt")
}.start()
}
// ¡Posibilidad de quedarse sin memoria!
}

✨ Ventajas de las Coroutines

1. Código conciso

// ✅ Coroutines (fácil de leer e intuitivo)
suspend fun fetchUser(userId: String): User? {
val result = fetchFromNetwork(userId) ?: return null
val profile = fetchProfile(result.id) ?: return null
val posts = fetchPosts(profile.id)
return User(result, profile, posts)
}

2. Concurrencia ligera

// ✅ Miles de coroutines sin problema
suspend fun downloadFiles() {
coroutineScope {
repeat(10000) {
launch {
// ¡Coroutines ligeras!
downloadFile("file_$it.txt")
}
}
}
}

3. Concurrencia estructurada

// ✅ Gestión automática del ciclo de vida
suspend fun processData() {
coroutineScope {
val job1 = launch { task1() }
val job2 = launch { task2() }

// Se completa automáticamente cuando todas las tareas terminan
}
}

🎯 Ejemplos de uso real

Peticiones de red

// ¡Limpio y sin callbacks!
suspend fun loadUserData(userId: String): UserData {
val user = apiService.getUser(userId)
val posts = apiService.getPosts(userId)
val friends = apiService.getFriends(userId)

return UserData(user, posts, friends)
}

Actualización de UI

// Sin bloquear el hilo principal
suspend fun updateUI() {
val data = withContext(Dispatchers.IO) {
// Cargar datos en segundo plano
loadDataFromDatabase()
}

// Actualizar UI en el hilo principal
updateViews(data)
}

Procesamiento paralelo

// ¡Múltiples tareas simultáneamente!
suspend fun fetchAllData(): CombinedData = coroutineScope {
val users = async { fetchUsers() }
val products = async { fetchProducts() }
val orders = async { fetchOrders() }

CombinedData(
users.await(),
products.await(),
orders.await()
)
}

🔥 Coroutines vs Hilos

CaracterísticaCoroutinesHilos
PesoMuy ligerasPesados
Costo de creaciónCasi nuloCostoso
CantidadDecenas de miles posiblesLímite de miles
Costo de cambioBajoAlto
CancelaciónFácilDifícil

Comparación de rendimiento

// Coroutines: ejecutar 100,000
suspend fun testCoroutines() {
val time = measureTimeMillis {
coroutineScope {
repeat(100_000) {
launch {
delay(1000)
}
}
}
}
println("코루틴: ${time}ms") // ~1000ms
}

// Hilos: incluso con solo 10,000...
fun testThreads() {
val time = measureTimeMillis {
repeat(10_000) {
Thread {
Thread.sleep(1000)
}.start()
}
}
println("쓰레드: ${time}ms") // ¡Posible falta de memoria!
}

🎨 Conceptos clave de las Coroutines

Suspend (suspensión)

// Las funciones suspend solo se pueden llamar dentro de una coroutine
suspend fun doWork() {
delay(1000) // Esperar sin bloquear el hilo
println("작업 완료")
}

Concurrencia estructurada

// Gestión del ciclo de vida mediante relación padre-hijo
fun main() = runBlocking {
launch { // Padre
launch { // Hijo 1
delay(1000)
println("자식 1 완료")
}
launch { // Hijo 2
delay(2000)
println("자식 2 완료")
}
println("부모는 자식이 끝날 때까지 대기")
}
}

🤔 Preguntas frecuentes

Q1. ¿Son las coroutines un nuevo tipo de hilo?

A: ¡No! Las coroutines son unidades de trabajo ligeras que se ejecutan sobre los hilos.

// Es posible ejecutar múltiples coroutines en un solo hilo
fun main() = runBlocking {
repeat(3) { i ->
launch {
println("코루틴 $i: ${Thread.currentThread().name}")
}
}
}
// ¡Todas pueden ejecutarse en el mismo hilo!

Q2. ¿Debo hacer que todas las funciones sean suspend?

A: ¡No! Solo haga suspend las funciones que requieran operaciones asíncronas.

// ❌ No es necesario
suspend fun add(a: Int, b: Int) = a + b

// ✅ Es necesario
suspend fun fetchData() = withContext(Dispatchers.IO) {
loadFromNetwork()
}

Q3. ¿Cuándo debo usar coroutines?

A: ¡Úselas en estos casos!

// ✅ Peticiones de red
suspend fun loadData() = api.fetchData()

// ✅ Operaciones de base de datos
suspend fun saveUser(user: User) = db.insert(user)

// ✅ I/O de archivos
suspend fun readFile(path: String) = withContext(Dispatchers.IO) {
File(path).readText()
}

// ✅ Operaciones de cálculo prolongadas
suspend fun heavyComputation() = withContext(Dispatchers.Default) {
// Cálculos complejos
}

🎬 Para finalizar

¡Haga la programación asíncrona fácil con coroutines!

Resumen de conceptos clave:
✅ Escape del infierno de callbacks
✅ Concurrencia ligera
✅ Código intuitivo
✅ Ciclo de vida estructurado
✅ Mucho más eficiente que los hilos

Siguiente paso: ¡Pruebe a usar coroutines en Fundamentos de Coroutines!