rxjs - example - manejo de errores de efectos
rxjs-compat (3)
Tengo una pregunta muy básica relacionada con los efectos de @ngrx: ¿Cómo ignorar un error que ocurre durante la ejecución de un efecto de manera que no afecte la ejecución futura del efecto?
Mi situación es la siguiente: tengo una acción (INICIAR SESIÓN) y un efecto al escuchar esa acción. Si ocurre un error dentro de este efecto, quiero ignorarlo. Cuando LOGIN se envía una segunda vez después de este error, el efecto se debe ejecutar una segunda vez.
Mi primer intento de hacer esto fue:
@Effect()
login$ = this.actions$
.ofType(''LOGIN'')
.flatMap(async () => {
console.debug(''LOGIN'');
// throw an error
let x = [];x[0]();
})
.catch(err => {
console.error(''Error at login'', err);
return Observable.empty();
});
Despachando LOGIN la primera vez lanza y atrapa el error, como se esperaba. Sin embargo, si despacho LOGIN una segunda vez después, no pasa nada; El efecto no se ejecuta.
Por eso probé lo siguiente:
.catch(err => {
return this.login$;
});
, pero esto da lugar a un bucle sin fin ... ¿Sabe cómo detectar el error sin impedir la ejecución del efecto después?
La infraestructura ngrx
suscribe al efecto a través del proveedor importado en el NgModule
la aplicación utilizando EffectsModule.run
.
Cuando los errores y las catch
observables devuelven un obervable vacío, el observable compuesto se completa y sus suscriptores se cancelan, eso es parte del Contrato observable . Y esa es la razón por la que no ve más manejo de las acciones de LOGIN
en su efecto. El efecto se cancela cuando finaliza y la infraestructura ngrx
no vuelve a suscribirse.
Normalmente, tendría el manejo de errores dentro de flatMap
(ahora llamado mergeMap
):
import { Actions, Effect, toPayload } from "@ngrx/effects";
@Effect()
login$ = this.actions$
.ofType(''LOGIN'')
.map(toPayload)
.flatMap(payload => Observable
.from(Promise.reject(''Boom!''))
.catch(error => {
console.error(''Error at login'', error);
return Observable.empty();
})
});
La catch
compuesta en el observable interior verá un observable vacío aplanado / fusionado en el efecto, por lo que no se emitirá ninguna acción.
La secuencia de @Effect
está completando cuando se produce el error, impidiendo cualquier acción adicional.
La solución es cambiar a un flujo desechable. Si ocurre un error dentro del flujo desechable, está bien, ya que el flujo principal de @Effect
siempre permanece activo y las acciones futuras continúan ejecutándose.
@Effect()
login$ = this.actions$
.ofType(''LOGIN'')
.switchMap(action => {
// This is the disposable stream!
// Errors can safely occur in here without killing the original stream
return Rx.Observable.of(action)
.map(action => {
// Code here that throws an error
})
.catch(error => {
// You could also return an ''Error'' action here instead
return Observable.empty();
});
});
Más información sobre esta técnica en esta publicación del blog: La búsqueda de albóndigas: continuar las transmisiones de RxJS cuando se producen errores
así es como manejo la opción de ser notificado o no de los Observables que no encontraron ningún dato:
public listenCampaignValueChanged(emitOnEmpty: boolean = false): Observable<CampaignsModelExt> {
var campaignIdSelected$ = this.ngrxStore.select(store => store.appDb.uiState.campaign.campaignSelected)
var campaigns$ = this.ngrxStore.select(store => store.msDatabase.sdk.table_campaigns);
return campaignIdSelected$
.combineLatest(campaigns$, (campaignId: number, campaigns: List<CampaignsModelExt>) => {
return campaigns.find((i_campaign: CampaignsModelExt) => {
return i_campaign.getCampaignId() == campaignId;
});
}).flatMap(v => (v ? Observable.of(v) : ( emitOnEmpty ? Observable.of(v) : Observable.empty())));
}