0
votes

Opérateur RxJs avec exécution inconditionnelle à la fin de chaque itération

Existe-t-il un opérateur ou une composition Rx pour garantir la dernière exécution de la logique pour chaque émission observable?

Supposons le contexte suivant:

  1. séquence sans fin ou continue
  2. logique conditionnelle comme filter () pour ignorer certaines émissions
  3. un peu de logique à la fin de chaque itération dans un opérateur de type doAlways()

Veuillez vous référer aux commentaires numérotés dans l'exemple de code ci-dessous

Remarques:

  • finalize () exigerait que la séquence se termine (enfreint la p.1)
  • iif () ou normal if à l'intérieur de switchMap () est une option mais rend le code plus illisible

Extrait de code pour illustrer: L'étape (3) doit toujours s'exécuter, par itération, en dernier, c'est-à-dire que nous voulons toujours commencer et terminer la connexion dans un doAlways () -like opérateur au lieu de tap()

import { of, interval } from 'rxjs'; 
import { tap, filter, switchMap } from 'rxjs/operators';


const dataService = { update: (z) => of(z /*posts data to back-end*/) };

const sub = interval(1000).pipe(            // <-- (1)
  tap(a => console.log('starting', a)),
  filter(b => b % 100 === 0),               // <-- (2)
  switchMap(c => dataService.update(c)),
  tap(d => console.log('finishing', d))     // <-- (3) should execute always even (2)
)
.subscribe(x => console.log(x));


1 commentaires

OP souhaite que la notification atteigne appuyez sur même si elle a été filtrée auparavant. Ce ne sera pas possible cependant.


3 Réponses :


1
votes

Un tel opérateur n'existe pas, et c'est parce qu'il ne peut pas exister. Une fois que vous avez filtré une valeur, l'observable qui en résulte ne l'émet plus. Tout opérateur "en aval" ne connaît tout simplement pas son existence.

Pour illustrer, vous avez inclus un switchMap à un service, qui dépend de la valeur émise. Pour des raisons évidentes cet opérateur qui ne peut pas être appliqué logiquement s'il n'y a pas de valeur à activer.


Pensez à l'observable comme à un tapis roulant sur lequel vous placez des objets. Chaque opérateur est une pièce à travers laquelle passe la ceinture. À l'intérieur de chaque pièce, un travailleur peut décider quoi faire de chaque article: le modifier, le retirer, mettre de nouveaux articles à la place, etc. avant eux ou ce qui a été fait là-bas.

Pour réaliser ce que vous voulez, le travailleur de la dernière salle devrait être au courant d'un objet qu'il n'a jamais reçu, ce qui nécessiterait des connaissances supplémentaires qu'il ne possède pas.


5 commentaires

Parfois, je pense que RxJS est juste Factorio mais plus illisible et moins débuggable


Je trouve cela extrêmement confortable à lire par rapport aux méthodes plus traditionnelles :-) La débuggabilité est un peu un problème, mais j'ai toujours réussi avec la journalisation des taps. Mais les préférences personnelles diffèrent.


Confortable pour ceux qui connaissent les mots, bien sûr ;-)


Cependant, je faisais référence à ce jeu: factorio.com On pourrait recréer votre analogie dans ce jeu. Peut-être illisible n'est pas le meilleur terme, disons «compréhensible»


Oui, si vous ne comprenez pas les concepts ou les mots, c'est presque impossible à comprendre. Mais je préférerais que les gens l'apprennent plutôt que de le rejeter à cause de cela. Je vérifierai ce lien plus tard!



1
votes

Non, ce que vous demandez n'est pas possible car les notifications filtrées ne seront jamais transmises. Vous auriez besoin d'un type de notification totalement nouveau qui n'est consommé par aucun autre opérateur que celui que vous décrivez.

Voici maintenant une idée qui n'est probablement pas recommandée. Vous pouvez abuser de la notification error pour ignorer certains opérateurs, mais cela interférera avec toute autre gestion d'erreur, ce n'est donc pas quelque chose que vous devriez faire ...

const sub = interval(1000).pipe(            
  tap(a => console.log('starting', a)),
  mergeMap(b => b % 100 === 0 ? of(b) : throwError(b)),
  switchMap(c => dataService.update(c)),
  catchError(b => of(b)),
  tap(d => console.log('finishing', d))
)

Notez que nous n'utilisons pas de filtre mais mappons vers une notification next ou error en fonction de la condition. Les erreurs seront naturellement ignorées par la plupart des opérateurs et consommées par tap , subscribe ou catchError . C'est une façon de mettre une balise sur l'élément pour que les travailleurs sachent qu'ils ne doivent pas y toucher (dans l'analogie décrite par Ingo)


2 commentaires

C'est pourquoi la condition se lit comme suit: "comme filtre" et non "filtre". Filtrer pour arrête sûrement tout flux supplémentaire, mais le sens est un moyen de sauter par-dessus les étapes indésirables pour les étapes d'itération particulières sans rendre iif-s ou d'autres choses qui rendent le code trop bouclé. Si .doAlways () - comme existait - le problème serait résolu


Eh bien, un seul opérateur ne pouvait rien faire de tel, c'est plus une restriction en raison des types de notifications possibles



1
votes

Je le diviserais pour 2 flux avec partition , et ils les fusionneraient.

https://stackblitz.com/edit/rxjs-8z9jit?file=index.ts

import { of, interval, merge } from 'rxjs';
import { tap, switchMap, partition, share } from 'rxjs/operators';

const dataService = { update: z => of(z /*posts data to back-end*/) };

const [sub, rest] = interval(1000).pipe(
  tap(a => console.log('starting', a)),
  share(),
  partition(b => b % 2 === 0)
);

merge(
    sub.pipe(
      switchMap(c => dataService.update(c)),
      tap(() => console.log('Ok'))
    ), 
    rest
)
.pipe(tap(d => console.log('finishing', d)))
.subscribe(x => console.log(x));

p >


0 commentaires