Utilizar operadores y evitar anidar suscripciones in RxJS, RxSwift or RxJava

Utilizar operadores y evitar anidar suscripciones in RxJS, RxSwift or RxJava

Las subscripciones anidadas es un anti-patrón que comúnmente se encuentra en implementaciones con código Rx.
Fácilmente se puede caer en este anti-patrón cuando la cantidad de código incrementa, cuando se requiere hacer un ajuste o una modificación.


Veamos el común ejemplo de subscripciones anidadas:

network.getToken(apiKey)
.subscribe(token => {
if (token.isValid) {
cache.storeToken(token)
.subscribe(saved =>
console.log('Token stored: ', saved));
}
});

¿Por qué es una práctica no recomendada?

  • Es un mal uso de los componentes.
  • Induce memory leaks.
  • Induce comportamientos no esperados.
La subscripción en Rx ha sido diseñada para vincular un Observer a un Observable (una fuente emisora de ítems).
Anidar una subscripción produce la vinculación de un Observable a otro Observable, es decir, se intentan usar un Observable como un Observer. Esto es un uso inadecuado de dichos componentes en Rx.
Únicamente Subjects son diseñados con esa capacidad y sin embargo se deben usar solo en casos especiales.




La subscripción se debe usar para vincular un Observer a un Observable, no para intentar orquestar operaciones, la tarea de orquestar operaciones se hace a través de los operadores.

Una subscripción es creada cada vez que se vincula observer code a una fuente (source). Debido a que la subscripción consume recursos de memoria, debe ser liberada una vez ya no sea necesaria. Anidar subscripciones hace que dichas subscripciones se hagan difíciles de detectar y controlar, lo cual puede derivar en fugas de memoria (memory leaks).

Cuando dos tareas se vinculan a través de subscripciones anidadas, no hay garantías en el orden de ejecución de una tarea con respecto a otra, es decir, no se garantiza una correcta orquestación de las tareas. Para dicho propósito existen los operadores tales como flatMap, concatMap, switchMap, etc.

¿Cómo corregir las subscripciones anidadas?

A través de la aplicación de los operadores de orquestación. En la tabla siguiente se muestran algunos de los más usados.
El código anterior podría ser corregido así:

En RxJS

network.getToken(apikey)
.pipe(
filter(token => token.isValid),
concatMap(token => this.cache.storeToken(token))
)
.subscribe(saved =>
console.log('Token stored: ', saved));

En RxSwift

network.getToken(apikey)

    .filter { token in token.isValid }

    .concatMap { token in self.cache.storeToken(token) }

    .subscribe(onNext: { saved in

        print("Token stored: \(saved)")

    })

    .disposed(by: disposeBag)


En RxJava

network.getToken(apiKey)
.filter(Token::isValid)
.concatMap(token -> this.cache.storeToken(token))
.subscribe(saved ->
Log.d(TAG, "Token stored: " + saved));


Subscripciones anidadas ocultas

Tenga cuidado con aquellos escenarios en donde la subscripción anidada no es tan evidente. Algunos casos se muestran a continuación.

Caso 1: A pesar que el código parece organizado, se está anidando la subscripción a través del método storeToken.

getToken(apikey: string) {
this.network.getToken(apikey)
.subscribe(token => {
this.storeToken(token);
});
}

storeToken(token: Token) {
if (token.isValid) {
this.cache.storeToken(token)
.subscribe(saved =>
console.log('Token stored: ', saved));
}
}

Caso 2: En este caso se utiliza Subject para transmitir el mensaje, pero se está sobreutilizando. El mismo resultado se podría obtener solo con un Observable.

const oneSubject = new Subject();
const twoSubject = new Subject();

oneSubject.subscribe(message => console.log(message));

twoSubject.subscribe(message => {
oneSubject.next(message);
});

twoSubject.next("A");
twoSubject.next("B");
twoSubject.next("C");

Aplicar un minucioso code review o linters en las herramientas, podría servir como filtro para evitar dicho anti-patrón.

Espero que este artículo sea de utilidad y haya permitido aclarar dudas sobre subscripciones anidadas.

Si desea mayor informarción, les recomiendo la lectura del libro guía:

The Clean Way to Use Rx, versión tanto en Inglés como Español.



Previous
Next Post »
Thanks for your comment