【问题标题】:How to parallel api calls and keep the order of the responses in a list for the ui to present (RxJS Observables)如何并行 api 调用并将响应的顺序保留在列表中以供 ui 呈现(RxJS Observables)
【发布时间】:2017-09-13 12:50:18
【问题描述】:

挑战!

我的问题如下:

我有一个获取 Observable 的函数,需要丰富人员数据并使用 Observable 更新观察者

哪个 Person 对象看起来像:

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 时,如果第一个 id 需要 2 秒响应,而后一个 id 只需要 1 秒,所以我失去了我的订单...我需要保持我最初的订单得到了函数参数...我怎样才能让它并行以获得更好的性能并保持我的订单?

【问题讨论】:

    标签: javascript angular typescript rxjs observable


    【解决方案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};
        };
        // ...
    

    【讨论】:

    • 那很好......但这是否也同时发生......?另外,在这种情况下,我总是因为某种原因得到另一个空对象......:/我试图弄清楚这一点
    • 是的,仍然并行。我只是建议更改回调的内容,没有其他会影响 API 使用的建议。
    【解决方案2】:

    您可以生成一个新的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));
    

    这里的可读性非常糟糕,但我将所有内容都放在一个块中,这样你就可以看到它们是如何链接在一起的

    【讨论】:

      猜你喜欢
      • 2017-04-28
      • 1970-01-01
      • 1970-01-01
      • 2021-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-22
      • 1970-01-01
      相关资源
      最近更新 更多