Programación reactiva frente a proactiva con RxJS

Publicado el

(Ben Copeland) (25 de marzo , 2020)

Si usamos bibliotecas ReactiveX como RxJS, sabemos que técnicamente estamos implementando una solución de programación reactiva: usamos el concepto de flujos observables de RxJS y nuestro código reacciona a nuevos eventos en el arroyo. Sin embargo, incluso si usamos una biblioteca reactiva, es posible diseñar nuestro software de acuerdo con un paradigma más proactivo que reactivo. Sin embargo, estas bibliotecas nos sirven mucho mejor si tenemos un estado de ánimo reactivo.

Tomemos un momento para identificar la distinción entre un enfoque proactivo y reactivo. Por proactivo me refiero a un enfoque en el que el editor (o sujeto) conoce a los suscriptores externos y sus estados deseados, y envía actualizaciones a los suscriptores conocidos. El acoplamiento con el consumidor ocurre en el lado del editor. Por reactivo me refiero a un enfoque en el que el suscriptor recibe una actualización y la maneja por sí solo.

Considere un escenario reciente que encontramos en IQueue para Clínicas. Tenemos una aplicación Angular que usa mucho RxJS y queríamos publicar cada cambio que aparece en el ngOnChanges enlace del ciclo de vida de un componente a una transmisión que los suscriptores podrían consumir.

Aquí hay un ejemplo simplificado, en el que tenemos un componente con tres @Input propiedades, y tenemos algunos componentes que quieren realizar un seguimiento de un objeto que tiene el valor más reciente de cada uno de las tres propiedades.

interface IChanges { a: string; b: string; c: string; }

@Component({...})
class SomeComponent {
@Input() a;
@Input() b;
@Input() c;

changes$: BehaviorSubject = new BehaviorSubject({
a: null,
b: null,
c: null,
});

allLatestChanges: IChanges;

ngOnChanges(changes: SimpleChanges) {
if (changes.a) {
changes$.next(Object.assign(allLatestChanges, { a: changes.a.currentValue });
}
if (changes.b) {
changes$.next(Object.assign(allLatestChanges, { b: changes.b.currentValue });
}
if (changes.c) {
changes$.next(Object.assign(allLatestChanges, { c: changes.c.currentValue });
}
}
}

En el ejemplo anterior, creamos un objeto con cada una de las tres propiedades posibles para el almacenamiento en caché, luego en ngOnChanges, fusionamos las actualizaciones de cualquiera de las tres propiedades en ese objeto y actualizamos la secuencia con el último valor del objeto combinado. Este es un enfoque proactivo: el sujeto conoce la intención del observador. Si se agrega otro observador que tiene una intención diferente, o si la intención del observador actual cambia, la implementación del sujeto debe cambiarse.

El enfoque anterior es esencialmente con lo que comenzamos, luego fuimos para refactorizarlo para hacerlo más reactivo. Para que eso sucediera, necesitábamos que el tema simplemente publicara el último valor sin ningún conocimiento de los consumidores. La siguiente es una versión actualizada del componente anterior que solo publica actualizaciones actuales.

@Component({...})
class SomeComponent {
@Input() a;
@Input() b;
@Input() c;

changes$: Subject = new Subject();

ngOnChanges(changes: SimpleChanges) {
changes$.next(changes);
}
}

Y luego manejamos la agregación en un solo objeto en la clase que consume el observable.

defaultAcc = {
a: null,
b: null,
c: null,
}

allChanges$ = changes$
.pipe(
scan((acc, curr) =>
Object.assign(acc, Object.fromEntries(
Object.entries(curr).map((k,v) => { [k]: v.currentValue })
)
)), defaultAcc
);

Ahora, el observable solo publica nuevos eventos, y el suscriptor está aprovechando uno de los muchos potentes tubos de RxJS llamado escaneo para mantener un estado acumulado de los valores más recientes de todas las propiedades posibles que vienen a través de la secuencia. Piense en el escaneo como una función de reducción que genera el último valor acumulado en cada evento. Entonces, después de la refactorización, tenemos un asunto de propósito general que distribuye todos los posibles eventos ngOnChanges. Si se agregan más @Inputs, naturalmente también los enviará. Cualquier suscriptor que esté escuchando la transmisión puede hacer lo que crea conveniente con la transmisión de eventos, ya sea acumulando un objeto de todos los eventos o solo escuchando los cambios en una sola propiedad.

Mientras que RxJS brinda Si tenemos las herramientas para hacer programación reactiva, todavía es posible diseñar código que siga un paradigma más proactivo. Para ser más reactivos, podemos mantener nuestra publicación lo más agnóstica posible y permitir que las suscripciones implementen una lógica más complicada.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *