我如何 return 一个 Observable 与其他 Observable 的数据一起使用他在其他 Observable 中的 return 值

How can I return an Observable with data of other Observables using his return value in the others

我有一个项目,我想在其中 return Observable of Hero 对象。 我的英雄们有多个 id 属性来从其他 Observables 获取数据作为 SkillRarity。我的 Observables 来自 AngularFire 库。这是我的英雄 class 我现在拥有的东西:

export class Hero extends Serializable {
  id?: string;
  name?: string;
  description?: string;
  idRarity?: string;
  rarity?: Rarity;
  stats: Stats;
  idSkill1?: string;
  idSkill2?: string;
  skill1?: Skill;
  skill2?: Skill;
  visual?: string;
  dateAdded?: Date;

  constructor() {
    super();
    this.stats = {};
    this.visual = "assets/images/placeholder.jpg";
  }
  ...

}

  // functions from the HeroService
  getHeroes(): Observable<Hero[]> {
    return this.db.collection<JsonArray>(HeroService.url)
      .valueChanges()
      .pipe(
        map(documents => {
          return documents.map(data=> {
            return this.getHeroFromData(data);
          });
        })
      );
  }

  getHero(id: string): Observable<Hero | undefined> {
    // Returns hero|undefined observable
    return this.getHeroDocument(id).valueChanges()
      .pipe(
        map(data => {
          return data ? this.getHeroFromData(data) : undefined
        })
      );
  }

  getHeroFromData(data:JsonArray) {
    let hero = new Hero().fromJSON(data);
    if (hero.idSkill1 && hero.idSkill2 && hero.idRarity) {
      this.skillService.getSkill(hero.idSkill1).subscribe(skill => hero.skill1 = skill);
      this.skillService.getSkill(hero.idSkill2).subscribe(skill => hero.skill2 = skill);
      this.rarityService.getRarity(hero.idRarity).subscribe(rarity => hero.rarity = rarity);
    }
    return hero;
  }

我面临的问题是,当我的英雄 returned 时,我的稀有度和技能属性数据尚未设置。

有没有办法在 returning hero 对象之前等待从其他 Observable 接收到所有值?

RxJs 6.5+

forkJoin 将在这里满足您的要求。

了解 mergeMap

  // functions from the HeroService
  getHero(id: string): Observable<Hero | undefined> {
    // Returns hero|undefined observable
    return this.getHeroDocument(id)
      .valueChanges()
      .pipe(
        mergeMap((data) => {
          return data ? this.getHeroFromData(data) : of(undefined);
        })
      );
  }

  getHeroFromData(data: JsonArray): Observable<Hero> {
    let hero = new Hero().fromJSON(data);
    if (hero.idSkill1 && hero.idSkill2 && hero.idRarity) {
      forkJoin({
        skill1: this.skillService.getSkill(hero.idSkill1),
        skill2: this.skillService.getSkil1(hero.idSkill2),
        rarity: this.rarityService.getRarity(hero.idRarity),
      }).pipe(
        map((res) => {
          hero.skill1 = res.skill1;
          hero.skill2 = res.skill2;
          hero.rarity = res.rarity;

          return hero;
        })
      );
    }
  }

如果你的英雄和技能只加载一次,你应该考虑使用promises而不是observables。如果您不能将其更改为核心,则可以使用 .toPromise() 将可观察对象转换为承诺。然后你可以使用 Promise.all().

getHeroFromData(data:JsonArray): Promise<Hero> {
    let hero = new Hero().fromJSON(data);
    if (hero.idSkill1 && hero.idSkill2 && hero.idRarity) {
      const promise 1 = this.skillService.getSkill(hero.idSkill1).toPromise().then(skill => hero.skill1 = skill);
      const promise2 = this.skillService.getSkill(hero.idSkill2).subscribe(skill => hero.skill2 = skill);
      const promise3 = this.rarityService.getRarity(hero.idRarity).subscribe(rarity => hero.rarity = rarity);
      return Promise.all([promise1, promise2, promise3]).then(() => hero);
    } else {
      return Promise.resolve(hero);
    }        
  }

那么在调用该方法时必须使用then。因为 hero 是以异步方式构建的,所以你必须以异步方式处理 return 。使用 then().

经过几次尝试,我设法使用 zip 运算符

实现了它

这是我最后得到的。

  getHeroes(): Observable<Hero[]> {
    return this.db.collection<JsonArray>(HeroService.url)
      .valueChanges()
      .pipe(
        mergeMap((docs) => {
          return zip(docs.map((doc) => { return this.getHeroFromData(doc) }))
        })
      );
  }


  getHero(id: string): Observable<Hero | undefined> {
    return this.getHeroDocument(id).valueChanges()
      .pipe(
        mergeMap(data => { return data ? this.getHeroFromData(data) : of(undefined) })
      );
  }

  private getHeroFromData(data: JsonArray): Observable<Hero> {
    const hero = new Hero().fromJSON(data);
    if (hero.idSkill1 && hero.idSkill2 && hero.idRarity) {
      return zip(
        this.skillService.getSkill(hero.idSkill1),
        this.skillService.getSkill(hero.idSkill2),
        this.rarityService.getRarity(hero.idRarity)
      ).pipe(
        map(res => {
          console.log(res);
          hero.skill1 = res[0];
          hero.skill2 = res[1];
          hero.rarity = res[2]
          return hero;
        })
      )
    }
    return of(hero);
  }