Javascript 如何并行api调用并在列表中保持响应顺序,以便ui呈现(RxJS可观察)

Javascript 如何并行api调用并在列表中保持响应顺序,以便ui呈现(RxJS可观察),javascript,angular,typescript,rxjs,observable,Javascript,Angular,Typescript,Rxjs,Observable,挑战 我的问题如下: 我有一个可以观察到的函数,需要丰富个人数据,并用可观察的数据更新观察者 哪个Person对象看起来像: export interface Person { personId: string; children: Child[]; } export interface Child { childId: string; } export interface EnrichedPerson { personName: string; parsonCountr

挑战

我的问题如下:

我有一个可以观察到的函数,需要丰富个人数据,并用可观察的数据更新观察者

哪个Person对象看起来像:

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

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

export interface EnrichedChild {
  childName: string;
  childAge: number
}
EnrichPerson看起来像:

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

export interface Child {
  childId: string;
}
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([]);
      });
}
private myFunc(人员列表:可观察):无效{
//初始化listOfEnrichedPeople时,这将是更新为行为主题的最终对象
//“public currentListOfPeople=新行为主体([])
让我们来看看被剥削者的名单:EnrichedPerson[]=[];
listOfPeople.subscribe((人员:Person[])=>{
people.map((person:person,personIdx:number)=>{
//在这里我建立了一个新的丰富儿童列表,因为每个人都有这样一个列表
//对于每个孩子,我还需要执行一个api调用来获取其信息-您很快就会看到
let listofEnrichedChild:EnrichedChild[]=[];
//在这里,我列出了一些人的ID,因为我将执行一个api调用来告诉我他们的名字
让id:string[]=people.map((person:person)=>person.personId);
这个._peopleDBApi.getPeopleNames(id).subscribe((名称:string[])=>{
//在这里,我想如果我已经有了名字,我可以设置它
新增人员列表({
personName:names[personIdx],
帕森国家:“,
儿童姓名:[]});
//现在,我想为每个人列出孩子的名单,丰富他们的数据
person.childrenIds.map((child:child)=>{
//问题就在这里,getChildInfo api只根据id执行,不能接收列表,我需要保持顺序…所以在
此.u childrenDBApi.getChildInfo(child.childId).subscribe((childInfo:childInfo)=>{
已删除的子项列表({
childName:childInfo.name,
childAge:childInfo.age});
});
});
EnrichedPeople[personIdx].parsonCountry=person.country;
listOfEnrichedPeople[personIdx]。children=listOfEnrichedChildren;
});
});
this.currentListOfPeople.next(enrichedPeople列表);
},
错误=>{
console.log(错误);
self.listOfEnrichedPeople.next([]);
});
}

我的问题是当我调用children api时,原因I如果第一个id需要2秒才能响应,之后的id只需要1秒,那么我将失去订单…我需要保留我最初在函数参数中获得的订单…如何使其并行以获得更好的性能并同时保留订单?

使用
.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};
    };
    // ...

您可以生成一个新的
Observable
,其中包含从API(并行)获取每个孩子/人的结果以及数组中的原始索引

然后,您可以将所有这些结果展平到一个新数组中,按原始索引对它们进行排序并返回它们

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));
const getEnrichedChildren=(子对象:Person[]):可观察=>
//从子数组中创建一个可观察的
可观察到的(…儿童)
//映射到一个新的可观察对象,其中包含来自API和
//原始索引。使用flatMap合并API响应
.flatMap((child,index)=>peopleApi.getPerson(child.personId).map(experized=>({child:experized,index})))
//将该可观测数据的所有结果合并为一个包含
//EnrichedPerson和原始索引的数组
.toArray()
//将结果映射到EnrichedPerson的排序列表
.map(unorderedChildren=>unorderedChildren.sort(c=>c.index.map)(c=>c.child));

这里的可读性非常差,但我将所有内容都保存在一个块中,这样您就可以看到事物是如何链接在一起的了

这很好……但这也是并行发生的吗。。?另外,在这种情况下,我总是因为某种原因得到另一个空对象…:/i我试图弄明白这一点是的,仍然是并行的。我只是建议更改回调的内容,而不是其他会影响API使用的内容。