Introducción a Reactive Programming
Imagina una aplicación web típica, como una tienda en línea.
- Un usuario hace clic en un botón (para ver los detalles del producto).
- La aplicación debe enviar una solicitud a un servidor lento y esperar la respuesta.
- Solo cuando llega la respuesta, puede actualizar la página.
1. El problema fundamental: ¿por qué “reactivo”?
El modelo clásico (imperativo): La aplicación bloquea el flujo mientras espera la respuesta. ¡Ineficiente! Es como pedir en un restaurante y luego retener al camarero hasta que tu comida esté lista. Mientras tanto, no puede atender a otros clientes.
La solución reactiva: La aplicación le dice al servidor: “Envíame los datos cuando estén listos. No espero aquí, pero te dejo mi número de teléfono. Llámame, cuando tengas algo.” Entre tanto, puede ocuparse de otras tareas.
2. El principio central: flujos de datos asincronos
Reactive Programming se basa en dos ideas fundamentales:
- Asincronía: Las tareas se ejecutan “en segundo plano”. El programa principal no se bloquea ni espera el resultado.
- Flujos de datos (Streams): Todo puede verse como un flujo de eventos: clics del ratón, pulsaciones de teclado, solicitudes HTTP, resultados de base de datos, etc. Estos flujos pueden ser generados, transformados, filtrados y combinados.
3. La analogía: el modelo de suscripción (revista vs. YouTube)
Para entender la mentalidad reactiva, comparamos dos modelos:
| Imperativo (modelo de consulta) | Reactivo (modelo de suscripción) | |
|---|---|---|
| Analogía | Comprar una revista | Suscribirse a un canal de YouTube |
| Acción | Vas al quiosco y preguntas activamente por el número más reciente. (Pull) | Haces clic en “Suscribirse”. (Push) |
| Futuro | La semana que viene tienes que volver al quiosco para obtener el nuevo número. | Recibes una notificación tan pronto como se publica un nuevo vídeo. |
| Ventaja | Obtienes lo que quieres de inmediato. | No te pierdes nada y no tienes que estar comprobando constantemente. El contenido llega a ti. |
Reactive Programming funciona con el modelo de suscripción (principio Push). Te suscribes a una fuente de datos y recibes una notificación cuando hay nuevos datos disponibles.
4. Los actores principales: Observer y Observable
Esta abstracción se utiliza en casi todos los frameworks reactivos (como RxJava, Project Reactor).
-
Observable (o Publisher):
- ¿Quién es? La fuente de datos. Produce un flujo de eventos o datos.
- Ejemplos: Un botón (produce eventos de clic), un sensor (produce lecturas), una respuesta del servidor.
- Tarea: Se encarga de quién está interesado en sus datos (sus “suscriptores”).
-
Observer (o Subscriber):
- ¿Quién es? El consumidor de datos. Está interesado en los datos del Observable.
- Ejemplos: La función que reacciona a un clic; la lógica que evalúa una lectura de sensor.
- Tarea: Se suscribe a un Observable. Debe ser capaz de procesar tres tipos distintos de notificaciones.
5. Los tres callbacks: cómo reacciona el Observer
El Observer debe indicar al Observable qué hacer en cada evento. Lo hace implementando tres métodos:
-
onNext(T value)- ¿Cuándo? Se invoca cuando el Observable emite un nuevo elemento de datos.
- ¿Qué hacer? Procesa el elemento recibido (por ejemplo, muestra los productos, registra el valor del sensor).
- Analogía YouTube: Se ha subido un nuevo vídeo.
-
onError(Throwable error)- ¿Cuándo? Se invoca cuando ocurre un error.
- ¿Qué hacer? Manejo de errores (por ejemplo, muestra un mensaje de error al usuario, reintenta).
- Analogía YouTube: Hubo un error al subir el vídeo.
-
onComplete()- ¿Cuándo? Se invoca cuando el Observable no enviará más datos. El flujo ha terminado.
- ¿Qué hacer? Tareas de limpieza.
- Analogía YouTube: El youtuber ha eliminado su canal. Definitivamente no habrá más vídeos nuevos.
6. Un ejemplo de código (conceptual)
Imagina que queremos registrar cada clic del ratón en una página web.
Enfoque imperativo (pseudocódigo)
// Preguntamos activamente (PULL)
while (true) {
if (mouseWasClicked()) { // ¡Se bloquea hasta que se produce un clic!
const clickEvent = getClickEvent(); // Obtiene el clic
console.log('Click at: ', clickEvent.position);
}
}
// Problema: Todo el bucle se bloquea y solo espera clics.
Enfoque reactivo (pseudocódigo con RxJS)
// Nos suscribimos a la fuente de datos de clics (PUSH)
fromEvent(document, 'click') // Crea un Observable a partir de eventos de clic
.subscribe( // Suscribirse y definir los callbacks
(clickEvent) => { // onNext: qué sucede en cada clic
console.log('Click at: ', clickEvent.clientX, clickEvent.clientY);
},
(error) => { // onError: qué sucede en caso de error
console.error('Something went wrong: ', error);
},
() => { // onComplete: qué sucede al final (nunca se invoca aquí, ya que los clics nunca se detienen)
console.log('No more clicks. Goodbye!');
}
);
// Ventaja: El hilo principal no se bloquea y puede hacer otras cosas.
// La función callback solo se invoca cuando hay un evento real.
7. ¿Por qué es tan poderoso? ¡Operadores!
La verdadera potencia radica en la capacidad de manipular estos flujos de datos con operadores, similar a map, filter, reduce en programación funcional.
filter: Deja pasar solo ciertos eventos. (Por ejemplo: solo clics dentro de un div específico)map: Transforma eventos. (Por ejemplo: convierte un evento de clic en un objeto{x: 10, y: 20})debounceTime: “Debouncea” eventos. (Por ejemplo: procesa solo el último clic en una secuencia rápida de clics, genial para campos de búsqueda)merge: Combina múltiples flujos. (Por ejemplo: une clics y pulsaciones de teclado en un flujo común)
fromEvent(document, 'click')
.pipe(
filter(event => event.target.id === 'myButton'), // Solo clics en #myButton
debounceTime(250), // Espera 250ms entre clics
map(event => { return {x: event.clientX, y: event.clientY}; }) // Transforma el evento
)
.subscribe(coord => console.log('Clicked at: ', coord));
Resumen
- Reactive Programming es un paradigma para el procesamiento no bloqueante y asincrónico de flujos de datos.
- Sigue el modelo Push o de suscripción: el consumidor de datos (Observer) se suscribe a una fuente de datos (Observable).
- El Observer reacciona a tres tipos de eventos: nuevos datos (
onNext), errores (onError) y finalización (onComplete). - A través de operadores (como
filter,map) los flujos de datos pueden transformarse y combinarse de manera elegante. - Ventajas: mejor uso de recursos (sin bloqueos), manejo elegante de eventos asincronos, composición más sencilla de lógicas complejas.

