Sobes.tech
Middle

¿Qué capacidades ofrece Kotlin para trabajar con operaciones asíncronas?

sobes.tech AI

Respuesta de la IA

Kotlin proporciona corrutinas para simplificar operaciones asíncronas, ofreciendo hilos ligeros que no bloquean el hilo principal.

Funciones clave:

  • Funciones de Suspensión: Funciones con la palabra clave suspend, que pueden ser suspendidas y reanudadas. No bloquean el hilo en el que se ejecutan.
    suspend fun fetchData(): String {
        delay(1000) // Simula una operación larga
        return "Datos recibidos"
    }
    
  • Constructores de Corrutinas: Funciones utilizadas para lanzar corrutinas:
    • launch: Inicia una nueva corrutina y devuelve un Job (no bloqueante).
      GlobalScope.launch {
          println("Corrutina lanzada")
      }
      
    • async: Inicia una nueva corrutina y devuelve un Deferred<T> (bloqueante al llamar a .await()).
      val deferred = GlobalScope.async {
          fetchData()
      }
      // ...
      val result = deferred.await() // Espera el resultado
      
    • runBlocking: Bloquea el hilo actual hasta que la corrutina termine (usado en puentes entre código bloqueante y no bloqueante, por ejemplo, en pruebas).
      runBlocking {
          val data = fetchData()
          println(data)
      }
      
  • Contexto de Corrutina: Conjunto de elementos que definen el comportamiento de la corrutina, incluyendo Dispatcher y Job.
    • Dispatcher: Define el hilo o grupo de hilos donde se ejecuta la corrutina.
  • Dispatchers: Objetos integrados para gestionar hilos:
    • Dispatchers.Default: Grupo de hilos de fondo compartido.
    • Dispatchers.IO: Optimizado para entrada/salida (operaciones de disco, red).
    • Dispatchers.Main: Hilo principal de UI (para Android, JavaFX, etc.).
    • Dispatchers.Unconfined: Ejecuta la corrutina en el hilo actual, pero puede reanudarse en cualquier hilo después de una suspensión.
    GlobalScope.launch(Dispatchers.IO) {
        // Se ejecuta en un hilo de entrada/salida
    }
    
  • Concurrencia Estructurada: Principio según el cual las corrutinas tienen un padre (CoroutineScope) y se cancelan automáticamente al cancelar el padre. Previene fugas de recursos y simplifica la gestión del ciclo de vida.
    // CoroutineScope gestiona automáticamente las corrutinas hijas
    val scope = CoroutineScope(Dispatchers.Default)
    val job = scope.launch {
        // ...
    }
    // Al cancelar el scope, se cancelan todas las corrutinas hijas
    scope.cancel()
    
  • Canales: Para transferencia segura de datos entre corrutinas. Pueden ser:
    • Sin búfer: La recepción solo ocurre después del envío.
    • Búfer: Permite enviar varios elementos antes de bloquear.
    • Conflated: Solo almacena el último elemento enviado.
    val channel = Channel<Int>()
    
    scope.launch {
        for (x in 1..5) channel.send(x)
        channel.close()
    }
    
    scope.launch {
        for (y in channel) println(y)
    }
    
  • Flow: Flujo de datos que puede emitir varios valores de forma asíncrona. Similar a RxJava Observable, pero más ligero e integrado con corrutinas.
    fun simpleFlow(): Flow<Int> = flow {
        for (i in 1..3) {
            delay(100)
            emit(i) // Envío de valor
        }
    }
    
    runBlocking {
        simpleFlow().collect { value -> println(value) } // Consumo de valores
    }
    

Estas capacidades permiten escribir código asíncrono en estilo imperativo, haciéndolo más legible y fácil de mantener en comparación con el uso de callbacks o Future.