2017-09-13 1 views
2

Défi!Comment mettre en parallèle les appels api et garder l'ordre des réponses dans une liste pour l'ui à présenter (RxJS Observables)

Ma question est la suivante:

J'ai une fonction qui obtient Observable et a besoin d'enrichir les données de la personne et mettre à jour un observateur avec un Observable

Quel objet Personne ressemble:

export interface Person { 
    personId: string; 
    children: Child[]; 
} 

export interface Child { 
    childId: string; 
} 

et EnrichPerson ressemble:

export interface EnrichedPerson { 
    personName: string; 
    parsonCountry: string; 
    children: EnrichedChild[] 
} 

export interface EnrichedChild { 
    childName: string; 
    childAge: number 
} 

Alors, ce que je l'ai fait est la suivante:

private myFunc(listOfPeople: Observable<Person[]>): void { 

    // initializing listOfEnrichedPeople , this will be the final object that will be updated to the behaviour subject 
    // "public currentListOfPeople = new BehaviorSubject<EnrichedPerson[]>([]);" 

    let listOfEnrichedPeople: EnrichedPerson[] = []; 

    listOfPeople.subscribe((people: Person[]) => { 
     people.map((person: Person, personIdx: number) => { 
      // here im setting up a new list of enriched children list cause each person have a list like this 
      // and for each of the children I need to perform also an api call to get its info - youll see soon 
      let listOfEnrichedChildren: EnrichedChild[] = []; 
      // here im taking a list of the ids of the people, cause im gonna perform an api call that will give me their names 
      let ids: string[] = people.map((person: Person) => person.personId); 

      this._peopleDBApi.getPeopleNames(ids).subscribe((names: string[]) => { 
      // here I though if I already have the name I can set it up 
       listOfEnrichedPeople.push({ 
       personName: names[personIdx], 
       parsonCountry: "", 
       childrenNames: [] }); 

       // now for each person, i want to take its list of children and enrich their data 
       person.childrenIds.map((child: Child) => { 
       // the catch is here, the getChildInfo api only perform it per id and cant recieve a list, and I need to keep the order...so did this in the 
        this._childrenDBApi.getChildInfo(child.childId).subscribe((childInfo: ChildInfo) => { 
           listOfEnrichedChildren.push({ 
           childName: childInfo.name, 
           childAge: childInfo.age}); 
        }); 
       }); 
       listOfEnrichedPeople[personIdx].parsonCountry = person.country; 
       listOfEnrichedPeople[personIdx].children = listOfEnrichedChildren; 
      }); 
     }); 
     this.currentListOfPeople.next(listOfEnrichedPeople); 
     }, 
     error => { 
     console.log(error); 
     self.listOfEnrichedPeople.next([]); 
     }); 
} 

mon problème est quand je fais l'appel pour les enfants api, parce que je si le premier identifiant prend 2 secondes pour répondre et une après seulement 1 sec si im perdre mon commander ... j'ai besoin de garder l'ordre que j'ai eu à l'origine dans le paramètre de la fonction ... comment puis-je le rendre parallèle pour de meilleures performances et aussi garder mes commandes?

Répondre

0

Utilisez l'argument d'index du rappel .map() et attribuez-le à la liste via cet index au lieu d'utiliser .push(). Ainsi, quel que soit le timing, la réponse de l'API sera assignée à la bonne position dans la liste.

person.childrenIds.map(({child: Child}, index) => { 
    this._childrenDBApi.getChildInfo(child.childId).subscribe((childInfo: ChildInfo) => { 
    listOfEnrichedChildren[index] = { 
     childName: childInfo.name, 
     childAge: childInfo.age}; 
    }; 
    // ... 
+0

c'est beau ... mais est-ce que ça se passe aussi en parallèle ..? aussi, dans ce cas, je reçois toujours un autre objet vide pour une raison quelconque ...:/Im essayant de comprendre cela –

+0

Oui, toujours en parallèle. Je suggère seulement une modification au contenu du rappel, rien d'autre qui affecterait l'utilisation de l'API. – Chris

0

Vous pouvez générer une nouvelle Observable contenant les résultats d'obtenir chaque enfant/personne de l'API (en parallèle) et l'indice d'origine dans le tableau.

Vous pouvez ensuite aplatir tous ces résultats en une seule nouvelle gamme, les trier par l'indice d'origine et de les retourner

const getEnrichedChildren = (children: Person[]): Observable<EnrichedPerson[]> => 
    //create an observable from the array of children 
    Observable.of(...children) 
    //map to a new observable containing both the result from the API and the 
    //original index. use flatMap to merge the API responses 
    .flatMap((child, index) => peopleApi.getPerson(child.personId).map(enriched => ({ child: enriched, index }))) 
    //combine all results from that observable into a single observable containing 
    //an array of EnrichedPerson AND original index 
    .toArray() 
    //map the result to a sorted list of EnrichedPerson 
    .map(unorderedChildren => unorderedChildren.sort(c => c.index).map(c => c.child)); 

La lisibilité de c'est assez terrible ici, mais j'ai tout gardé dans un bloc de sorte que vous pouvez voir comment les choses enchaînent