Introducción
En este tema, exploraremos las corrutinas en Kotlin, una característica poderosa para manejar la programación asíncrona de manera eficiente y sencilla. Las corrutinas permiten escribir código asíncrono de manera secuencial, lo que facilita la lectura y el mantenimiento del código.
Conceptos Clave
- Corrutinas: Son una forma de realizar tareas asíncronas sin bloquear el hilo principal.
 - Scope (Alcance): Define el contexto en el que se ejecutan las corrutinas.
 - Dispatcher (Despachador): Determina en qué hilo o hilos se ejecutará la corrutina.
 - Job: Representa una unidad de trabajo que se puede cancelar.
 - Deferred: Es un Job que puede devolver un resultado.
 
Configuración Inicial
Para usar corrutinas en Kotlin, necesitas agregar la dependencia de corrutinas en tu archivo build.gradle:
Ejemplo Básico de Corrutina
Lanzar una Corrutina
import kotlinx.coroutines.*
fun main() = runBlocking {
    launch {
        delay(1000L)
        println("Corrutina!")
    }
    println("Hola,")
}Explicación
runBlocking: Bloquea el hilo principal hasta que todas las corrutinas dentro de él se completen.launch: Inicia una nueva corrutina sin bloquear el hilo actual.delay: Suspende la corrutina durante el tiempo especificado sin bloquear el hilo.
Ejercicio Práctico
Ejercicio 1:
Escribe un programa que lance dos corrutinas. La primera debe imprimir "Primera Corrutina" después de 1 segundo y la segunda debe imprimir "Segunda Corrutina" después de 2 segundos.
Solución:
import kotlinx.coroutines.*
fun main() = runBlocking {
    launch {
        delay(1000L)
        println("Primera Corrutina")
    }
    launch {
        delay(2000L)
        println("Segunda Corrutina")
    }
    println("Inicio")
}Scope y Dispatcher
Scope
El scope define el contexto de la corrutina. Los scopes más comunes son GlobalScope, CoroutineScope y runBlocking.
Dispatcher
El dispatcher determina en qué hilo se ejecutará la corrutina. Los dispatchers más comunes son:
Dispatchers.Default: Utiliza un pool de hilos compartido.Dispatchers.IO: Optimizado para operaciones de entrada/salida.Dispatchers.Main: Utilizado para operaciones en el hilo principal (UI).
Ejemplo con Dispatcher
import kotlinx.coroutines.*
fun main() = runBlocking {
    launch(Dispatchers.Default) {
        println("Default Dispatcher: ${Thread.currentThread().name}")
    }
    launch(Dispatchers.IO) {
        println("IO Dispatcher: ${Thread.currentThread().name}")
    }
    launch(Dispatchers.Main) {
        println("Main Dispatcher: ${Thread.currentThread().name}")
    }
}Ejercicio Práctico
Ejercicio 2:
Modifica el programa anterior para que cada corrutina imprima un mensaje diferente y se ejecute en un dispatcher diferente.
Solución:
import kotlinx.coroutines.*
fun main() = runBlocking {
    launch(Dispatchers.Default) {
        println("Ejecutando en Default Dispatcher: ${Thread.currentThread().name}")
    }
    launch(Dispatchers.IO) {
        println("Ejecutando en IO Dispatcher: ${Thread.currentThread().name}")
    }
    launch(Dispatchers.Main) {
        println("Ejecutando en Main Dispatcher: ${Thread.currentThread().name}")
    }
}Job y Deferred
Job
Un Job representa una unidad de trabajo que puede ser cancelada.
import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        repeat(1000) { i ->
            println("Job: $i")
            delay(500L)
        }
    }
    delay(1300L)
    println("Cancelando el job")
    job.cancelAndJoin()
    println("Job cancelado")
}Deferred
Un Deferred es un Job que puede devolver un resultado.
import kotlinx.coroutines.*
fun main() = runBlocking {
    val deferred = async {
        delay(1000L)
        "Resultado"
    }
    println("Esperando el resultado...")
    val result = deferred.await()
    println("Resultado: $result")
}Ejercicio Práctico
Ejercicio 3:
Escribe un programa que lance un Job que imprima números del 1 al 5 con un retraso de 1 segundo entre cada número. Después de 3 segundos, cancela el Job.
Solución:
import kotlinx.coroutines.*
fun main() = runBlocking {
    val job = launch {
        repeat(5) { i ->
            println("Número: ${i + 1}")
            delay(1000L)
        }
    }
    delay(3000L)
    println("Cancelando el job")
    job.cancelAndJoin()
    println("Job cancelado")
}Conclusión
En esta sección, hemos aprendido los conceptos básicos de las corrutinas en Kotlin, incluyendo cómo lanzarlas, los diferentes scopes y dispatchers, y cómo manejar Job y Deferred. Las corrutinas son una herramienta poderosa para manejar la programación asíncrona de manera eficiente y legible. En el próximo módulo, exploraremos cómo aplicar estos conceptos en el desarrollo de aplicaciones Android con Kotlin.
Curso de Programación en Kotlin
Módulo 1: Introducción a Kotlin
- Introducción a Kotlin
 - Configuración del Entorno de Desarrollo
 - Conceptos Básicos de Kotlin: Variables y Tipos de Datos
 - Flujo de Control: Condicionales y Bucles
 - Funciones y Lambdas
 
Módulo 2: Programación Orientada a Objetos en Kotlin
- Clases y Objetos
 - Herencia e Interfaces
 - Modificadores de Visibilidad
 - Clases de Datos y Clases Selladas
 - Declaraciones de Objetos y Objetos Compañeros
 
Módulo 3: Características Avanzadas de Kotlin
- Colecciones y Genéricos
 - Funciones de Extensión
 - Funciones de Orden Superior y Programación Funcional
 - Corrutinas y Programación Asíncrona
 - DSL (Lenguaje Específico de Dominio) en Kotlin
 
Módulo 4: Kotlin para Desarrollo Android
- Introducción al Desarrollo Android con Kotlin
 - Construcción de Interfaces de Usuario
 - Manejo de Entrada del Usuario
 - Redes y Almacenamiento de Datos
 - Pruebas y Depuración
 
