Introducción a la programación asíncrona en Kotlin
Las aplicaciones modernas requieren alto rendimiento y capacidad de respuesta. La programación asíncrona en Kotlin es una herramienta poderosa que permite escribir código no bloqueante, utilizando eficientemente los recursos del sistema. A diferencia de la multitradicional, las corrutinas de Kotlin proporcionan hilos de ejecución ligeros que pueden suspenderse y reanudarse sin bloquear el hilo principal.
En esta guía, analizaremos los conceptos clave de la programación asíncrona en Kotlin: desde corrutinas básicas hasta patrones avanzados usando Flow y Channel. Aprenderás a crear aplicaciones asíncronas eficientes que funcionarán de manera rápida y confiable.
Fundamentos de las corrutinas en Kotlin
¿Qué es una corrutina?
Una corrutina (coroutine) es un hilo de ejecución ligero que puede suspenderse en un punto y reanudarse en otro. En Kotlin, las corrutinas se implementan a nivel de lenguaje usando la palabra clave suspend y constructores especiales.
Iniciando la primera corrutina
Para trabajar con corrutinas, es necesario agregar la dependencia en build.gradle.kts:
dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")}Ejemplo básico de inicio de una corrutina:
import kotlinx.coroutines.*
fun main() = runBlocking { launch { delay(1000L) println("¡Mundo!") } println("Hola,")}Resultado de la ejecución:
Hola,¡Mundo!Funciones suspend
Las funciones suspend son funciones que pueden pausar la ejecución de una corrutina sin bloquear el hilo. Se marcan con la palabra clave suspend:
suspend fun fetchUserData(): String { delay(2000L) // simulación de solicitud de red return "Datos del usuario"}
fun main() = runBlocking { val data = fetchUserData() println(data)}Gestión de corrutinas: Scope y Dispatchers
CoroutineScope
CoroutineScope define el ciclo de vida de las corrutinas. Todas las corrutinas se inician dentro de un ámbito específico. Si el ámbito se cancela, también se cancelan todas sus corrutinas hijas.
class MyViewModel : ViewModel() { private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob()) fun loadData() { scope.launch { val data = fetchData() updateUI(data) } } override fun onCleared() { super.onCleared() scope.cancel() }}Dispatchers: selección del hilo de ejecución
Los Dispatchers determinan en qué grupo de hilos se ejecutará la corrutina. Tipos principales:
Dispatchers.Main— para el hilo de UI (Android, JavaFX)Dispatchers.IO— para operaciones de entrada/salida (red, archivos)Dispatchers.Default— para tareas intensivas en CPUDispatchers.Unconfined— no está vinculado a un hilo específico
fun main() = runBlocking { launch(Dispatchers.IO) { println("Ejecutándose en IO: ${Thread.currentThread().name}") } launch(Dispatchers.Default) { println("Ejecutándose en Default: ${Thread.currentThread().name}") }}Técnicas avanzadas: async/await y cancelación de corrutinas
Ejecución paralela con async
El constructor async permite ejecutar varias tareas en paralelo y obtener resultados usando await():
suspend fun fetchDataFromApi1(): String { delay(1000L) return "Resultado API 1"}
suspend fun fetchDataFromApi2(): String { delay(1500L) return "Resultado API 2"}
fun main() = runBlocking { val time = measureTimeMillis { val result1 = async { fetchDataFromApi1() } val