Programación reactiva en Kotlin: el poder de kotlinx.coroutines y Flow
La programación reactiva es un paradigma orientado al procesamiento asíncrono de flujos de datos y la propagación de cambios. En el ecosistema de Kotlin, la herramienta principal para implementar patrones reactivos es la biblioteca kotlinx.coroutines, y más concretamente su componente Flow. A diferencia de RxJava, Flow está estrechamente integrado con las corrutinas, lo que hace que el código sea más conciso y seguro.
Conceptos fundamentales de Flow
Flow es un flujo de datos asíncrono frío que emite valores secuencialmente. Soporta:
- Flujos fríos — los datos comienzan a generarse solo cuando hay un suscriptor (operador terminal).
- Contexto de ejecución — el flujo puede cambiar entre diferentes despachadores de corrutinas.
- Cancelación mediante corrutinas — la cancelación de la corrutina padre finaliza automáticamente el Flow.
Instalación y configuración
Para usar Flow, agregue la dependencia en build.gradle.kts:
dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")}Creación y consumo de Flow
Creemos un flujo simple de números con retardo:
import kotlinx.coroutines.*import kotlinx.coroutines.flow.*
fun simpleFlow(): Flow<Int> = flow { for (i in 1..5) { delay(100) // simulación de trabajo asíncrono emit(i) // emitimos el valor }}
fun main() = runBlocking { simpleFlow().collect { value -> println("Recibido: $value") }}En este ejemplo, flow { ... } crea un flujo frío, y collect es el operador terminal que inicia la ejecución.
Transformación de datos con operadores
Flow proporciona un rico conjunto de operadores, similar a RxJava:
map— transforma cada valor.filter— filtra valores según una condición.flatMapConcat— combina flujos secuencialmente.catch— maneja excepciones dentro del flujo.
Ejemplo con operadores:
fun transformFlow(): Flow<String> = flow { emit("Kotlin") emit("Java") emit("Python")}.map { it.lowercase() } .filter { it.startsWith("k") }
fun main() = runBlocking { transformFlow().collect { println(it) } // Salida: kotlin}Manejo de errores y finalización
Flow permite manejar excepciones de forma elegante con catch y ejecutar acciones finales mediante onCompletion:
fun errorProneFlow(): Flow<Int> = flow { emit(1) emit(2) throw RuntimeException("¡Error!") emit(3) // no se ejecutará}
fun main() = runBlocking { errorProneFlow() .catch { e -> println("Error capturado: ${e.message}") } .onCompletion { println("Flujo finalizado") } .collect { println(it) }}// Salida: 1, 2, Error capturado: ¡Error!, Flujo finalizadoFlujos calientes: StateFlow y SharedFlow
A diferencia del Flow frío, StateFlow y SharedFlow son flujos calientes que almacenan estado y pueden tener múltiples suscriptores.
StateFlow
Se utiliza para almacenar y observar el estado (por ejemplo, en ViewModel):
class CounterViewModel { private val _counter = MutableStateFlow(0) val counter: StateFlow<Int> = _counter.asStateFlow()
fun increment() { _counter.value++ }}
// En Activity o FragmentlifecycleScope.launch { viewModel.counter.collect { value -> textView.text = "Contador: $value" }}SharedFlow
Adecuado para
También en la biblioteca
Colas de tareas para PHP: RabbitMQ, Gearman y PhpAmqpLib
ESLint + Plugins de TypeScript para TypeScript: guía completa con ejemplos
Trabajo con tokens JWT en TypeScript: librería jsonwebtoken y guía completa
ORM para JavaScript (Node.js): comparación y ejemplos
Trabajo con S3 y almacenamiento en la nube en Kotlin: AWS SDK y MinIO