web-dev-qa-db-fra.com

Comment envoyer une erreur sur un sujet de comportement et continuer le flux?

À une extrémité, j'ai un flux qui peut parfois générer une erreur:

this.behaviorSubject.error(error)

Plus tard, cependant, je veux continuer le flux:

this.behaviorSubject.next(anotherValue)

à l'autre bout, j'ai un abonné abonné à behaviorSubject.asObservable().

Dans l'abonnement, je gère la valeur et l'erreur:

.subscribe( 
   ( value ) =>  {  /* ok */ },
   ( error ) =>  {  /* some error */ }
);

Je veux que l'effet soit le même qu'un simple rappel onSuccess et onError, où onError est appelé chaque fois qu'une erreur se produit et n'empêche pas le futur onSuccess appels en cours. Comment faire cela avec RXJS?

J'ai examiné les captures, mais cela semble simplement empêcher les erreurs d'être appelées par les abonnés.

24
David

Réponse courte: Ce n'est pas possible.

Comment travailler avec ceci: Le concept de base de RxJS est que tout appel error ou complete- sera fondamentalement = "tuer" un flux. Ce concept ne vous oblige pas "juste à contourner les erreurs ici et là à votre guise" mais à gérer correctement les erreurs et le flux de données dans votre application. Un BehaviorSubject par exemple est généralement destiné à contenir des données, mais il ne doit pas être utilisé pour inclure également le processus de récupération/création données et gérer les erreurs possibles qui pourraient survenir lors de la récupération des données.

Donc, si vous voulez suivre le livre, vous devez diviser votre flux en deux parties :

  1. Récupération/création des données : Un flux, qui s'exécutera une fois puis se terminera et/ou lancera une erreur chaque fois qu'elle se produira. Lorsque les données sont récupérées, elles seront envoyées au magasin.
  2. Le magasin (par exemple, comme dans votre cas: un tas de BehaviorSubjects): Seules des données valides arrivent dans le magasin, cela signifie qu'aucune gestion des erreurs n'est effectuée ici et toutes les parties qui dépendent du magasin peuvent faire confiance au magasin qui contient les données correctes.

À titre d'exemple, votre flux de données pourrait ressembler à ceci (sous forme d'esquisse):

store.ts

dataStore: BehaviorSubject<IData> = new BehaviorSubject<IData>();
errorMessage: BehaviorSubject<IErrorMsg> = new BehaviorSubject<IErrorMsg>();

data-retrieval.ts

fetchDataById(id: string) {
    httpService.get(`some/rest/endpoint/${id}`)
        .subscribe(handleData, handleError);
}

handleData(data: IData) {
    errorMessage.next(null);
    dataStore.next(data);
}

handleError(error: Error) {
    errorMessage.next(error.message);
    dataStore.next(null);
}

"Mais cela ressemble à beaucoup de frais généraux ..." - Certes, mais il garantit un flux de données propre et facile à comprendre au sein de votre application, qui est facile à tester et à maintenir . Il existe également des concepts de magasin prêts à l'emploi tels que ngrx ou redux qui pourraient être utilisés.

21
olsn

Rx est fondamentalement construit sur le concept qu'un observable est soit actif, soit finalisé (onComplete ou onError). Lorsqu'un observable est en cours de finalisation, il se désabonne de son observable en amont. Non .catch Ne peut pas résoudre ce problème, il vous donne uniquement la possibilité de mapper l'erreur à autre chose.

Rx.Observable.interval(500)
  .mergeMap(i => i % 3 == 2 ? Rx.Observable.throw(new Error('kboom')) : Rx.Observable.of(i))
  .catch(err => Rx.Observable.of(err.message))
  .subscribe(
    val => console.log('val: ' + val),
    err => console.log('err: ' + err),
    () => console.log('stream completed')
  )

A noter que cet exemple se termine après 3 émissions au lieu de 5

Lorsque vous invoquez this.behaviorSubject.error(error) cela finalisera en interne l'Observable contenu dans votre Sujet. Si vous voulez en quelque sorte émettre des erreurs, vous devez faire de vos erreurs des valeurs sans erreur:

this.behaviorSubject.next({ value: 'somevalue' });
this.behaviorSubject.next({ error: error });
this.behaviorSubject.next({ value: 'somevalue' });

Ensuite, vous pouvez distinguer, en fonction des propriétés de votre valeur émise, l'action à entreprendre.

4
Mark van Straten

Cela peut ne pas fonctionner pour votre situation, mais j'ai rencontré ce même problème lorsque nous travaillons avec Angular 2 car nous naviguerions entre les écrans et voudrions que le service réessaye une API et ne se contente pas d'appeler l'erreur Cela entraînerait en fait des problèmes plus importants car la fonction était appelée dans notre constructeur et la fonction d'erreur tentait de mettre à jour l'interface utilisateur qui n'était pas encore prête.

Ce que j'ai fait semble bien fonctionner et être assez propre. J'ai créé une réinitialisation du sujet dans le gestionnaire d'erreurs.

subject.subscribe( 
   ( value ) =>  {  /* ok */ },
   ( error ) =>  {  
      //handle error
      //reset subject
      this.subject = new Subject<any>();
   }
);

Cela fonctionne dans notre cas, car chaque fois que vous accédez à l'écran, de nouveaux abonnements sont supprimés de l'ancien écran, puis configurés dans le nouveau, de sorte que le nouveau sujet ne nuira à rien.

0
KickerKeeper