2
votes

Existe-t-il un opérateur qui fonctionne comme concatMap mais avec plusieurs observables internes

J'utilise un observable pour interroger ma base de données. Cette observable renverra un tableau avec tous les objets correspondants trouvés. Mon problème est que je veux mapper l'observable avec plus de détails que je récupérerai d'une autre API.

J'ai essayé concatMap mais il ne m'a permis d'imbriquer 1 observable dans l'observable initial

const usersAPI$: Observable<Users[]> = //  something
const assistsAPI$: Observable<Assists[]> = //  something

const modifiedAssists$ = assistsAPI$.find().pipe(
  concatMap(assists =>
    assists.map(assist => usersAPI$.get(assist.userID)
       .pipe(
          map(user => 
            assist = {...assist}, ...{userName: user.userName}
          )
        )
    )
  )
);

Vous pouvez voir un exemple de travail similaire ici, sur stackblitz, https://stackblitz.com/edit/rxjs-concatmap-issue-stackoverflow où le "résultat" est la manière correcte d'utiliser concatMap et le "result2" est la manière dont je m'attendais à ne pas fonctionner


4 commentaires

Vous pouvez utiliser mergeMap au lieu de concatMap .


@martin le mergeMap me pose le même problème que le concatMap . (Et ce sont en fait des opérateurs très similaires. Je recherche une solution différente)


Si mergeMap se comporte de la même manière, regardez ce que assistsAPI $ .find () car il n'émet probablement pas les éléments que vous attendez.


Le backend est FeathersJS. Je suis certain de ce que l'API $ .find () émet. Ce n'est pas le but ici. Mais je suis reconnaissant que vous essayiez d'aider, merci :)


3 Réponses :


0
votes

Vous pouvez utiliser combineLatest () (comme importation statique non pipe)

import {combineLatest} from 'rxjs';

const usersAPI$: Observable<Users[]> = //  something
const assistsAPI$: Observable<Assists[]> = //  something

const combinedObservable$ = combineLatest(usersAPI$,assistsAPI$, someOtherStuff$);

const modifiedAssists$ = combinedObservable$.pipe(
   map([usersApiValues, assistsAPIValues, someOtherStuffValues] => { 
      /** what you want here */
   }) 
);

Il suffit de prendre en compte que chacun des Les observables dans combineLatest doivent émettre au moins une fois (vous pouvez les démarrer avec un tableau vide par exemple)


1 commentaires

J'ai besoin de savoir combien de assistsAPIValues ​​ j'ai, avant de demander des usersApiValues ​​. Je dois transformer l'observable assistsAPI $ afin qu'il renvoie le même tableau du flux mais avec la propriété userName déjà attribuée dans chaque assist dans le tableau assists renvoyé par assistsAPI $



0
votes

Je crois que vous voulez mergeMap ?

Il mappe l'observable externe à l'observable interne résolue pour chaque émition externe.

const result = clicks.pipe(
  concatMap(ev =>
    interval(1000).pipe(take(4))
  )
);

const result2 = clicks.pipe(
  mergeMap(ev =>
    arr.map(() => interval(1000).pipe(take(4)))
  )
);
result.subscribe(x => {
  console.log(x)
});

result2.subscribe(x => {
  console.log(x)
});


1 commentaires

le mergeMap (comme le concatMap ) ne résout pas les observables créés par le arr.map () intérieur comme prétendu.



3
votes

Vous devez traiter votre tableau interne d'Observables d'une manière ou d'une autre. Vous pouvez utiliser forkJoin , merge ou concat selon vos besoins.

forkJoin

forkJoin traitera vos appels d'API internes en parallèle et retournera un tableau lorsque tous les appels d'API seront terminés. Notez que les observables internes doivent être terminés. Je pense que c'est ce qui devrait vous convenir dans votre application.

import { concat } from 'rxjs';

const modifiedAssists$ = assistsAPI$.find().pipe(
  concatMap(assists => concat(...assists.map(assist => someObservable )),
  // toArray() add the toArray operator if you need the result as an array
);

merge

merge s'abonnera à tous les appels d'API internes à la fois et émettre les résultats de tous les appels un par un à mesure qu'ils arrivent.

import { merge } from 'rxjs';

const modifiedAssists$ = assistsAPI$.find().pipe(
  concatMap(assists => merge(...assists.map(assist => someObservable )),
  // toArray() add the toArray operator if you need the result as an array
);

Cela vous donnera le résultat souhaité dans votre stackblitz, mais je pense que votre stackblitz est quelque peu trompeur et pas ce que vous recherchez dans l'exemple de code de votre question, car l'observable interne de votre stackblitz émet plusieurs fois et la sortie finale n'est pas un tableau. Si l'ordre des requêtes internes n'a pas d'importance, elles émettent toutes une valeur puis se terminent et vous avez besoin des résultats de toutes les requêtes sous forme de tableau, utilisez simplement forkJoin.

concat

concat s'abonnera à tous les appels d'API internes l'un après l'autre. Le prochain Observable de la séquence ne sera abonné qu'une fois le précédent terminé. L'exécution sera donc plus lente qu'avec forkJoin ou merge car la prochaine requête HTTP ne sera exécutée qu'après que la précédente a renvoyé une valeur. Utilisez cette option si les appels à votre userAPI doivent spécifiquement être effectués dans le même ordre que les assists dans votre tableau.

import { forkJoin } from 'rxjs';

const modifiedAssists$ = assistsAPI$.find().pipe(
  concatMap(assists => forkJoin(assists.map(assist => someObservable ))
);


1 commentaires

Je vous remercie! Votre solution concat fonctionnait plutôt bien pour le but que je voulais (plus avec le toArray () comme vous l'avez suggéré aussi)!