2017-09-13 38 views
2

挑戰!如何平行API調用,並保持列表中的響應順序爲UI(RxJS Observables)

我的問題是如下:

我有得到觀察到的,需要充實人的數據,並更新與可觀察到的

哪個人對象看起來像一個觀察者的函數:

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

export interface Child { 
    childId: string; 
} 

和EnrichPerson樣子:

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

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

所以,我做的事是這樣的:

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([]); 
     }); 
} 

我的問題是,當我讓孩子們API調用,它只有1秒打完,因爲我如果第一個ID需要2秒的響應和一個IM失去了我訂單...我需要保留原來在函數參數中獲得的訂單...我如何才能使其平行以獲得更好的性能並保持我的訂單?

回答

0

使用.map()回調的索引參數,並通過該索引分配給列表,而不是使用.push()。這樣,無論何時,API響應都會被分配到列表中的正確位置。

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

多數民衆贊成在尼斯...但是,這也發生在並行..?此外,在這種情況下,我總是從某個原因得到另一個空對象...:/ im試圖找出這個 –

+0

是的,仍然是平行的。我只是建議更改回調內容,而不會影響API的使用。 – Chris

0

可以生成包含從API(並行地)獲取每個子/人的結果的新Observable陣列中的原始索引。

然後,您可以拼合所有這些結果分解成一個新的數組,由原始指標進行排序,並返回他們

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)); 

這樣做的可讀性是非常可怕的這裏,但我已經把一切都在一個塊,所以你可以看到事情是如何鏈在一起的