如何平行API调用,并保持列表中的响应顺序为UI(RxJS Observables)
问题描述:
挑战!如何平行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失去了我订单...我需要保留原来在函数参数中获得的订单...我如何才能使其平行以获得更好的性能并保持我的订单?
答
使用.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};
};
// ...
答
可以生成包含从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));
这样做的可读性是非常可怕的这里,但我已经把一切都在一个块,所以你可以看到事情是如何链在一起的
多数民众赞成在尼斯...但是,这也发生在并行..?此外,在这种情况下,我总是从某个原因得到另一个空对象...:/ im试图找出这个 –
是的,仍然是平行的。我只是建议更改回调内容,而不会影响API的使用。 – Chris