如何使用rxjs运算符和转换链接多个从属订阅?

时间:2019-05-24 20:29:41

标签: angular rxjs

我有一个功能,应该从一个API提取数据,然后使用结果从多个其他api获取数据。所有这些操作都必须先进行数据提取,但是某些后续调用可以并行运行,而其他一些调用则需要一个或多个并行调用返回的数据才能运行。目前,通过在另一个订阅中进行三个订阅(对于正在计数的一个函数中的四个订阅),我已经完成了一些工作,但这对我来说有点不对劲,我读过的所有内容都说这是反模式或完全错误。我需要一种方法来使所有这些东西以正确的顺序运行(在需要顺序的情况下)并返回我的数据。

我研究了几个rxjs选项,我认为我需要使用forkJoin(对于可以并行运行的项目)和mergeMap(对于依赖forkJoin结果的项目)进行某种组合。当前的设置是调用我的服务并进行订阅,然后在该订阅中forkForJoin并调用其他几个函数,对其进行订阅,然后在其中进行两次调用并订阅其他调用。

getThingFromService(id: number): any {
  const drinks = {};
  this.thingService.getThingById(id)
    .subscribe((thing) => {
      this.thing = thing;
      forkJoin([
        this.getTea(thing.tea.id),
        this.getCoffee(thing.coffee.id),
        this.getSoda(thing.soda.id)
      ])
      .subscribe((data) => {
        drinks.tea = data[0];
        drinks.coffee = data[1];
        drinks.soda = data[2];
        this.getCoffeeTypeById(drinks.coffee.bean.id)
          .subscribe((bean) => {
            drinks.coffeeBean = bean;
          })
        this.getSodaTypeById(drinks.soda.flavors.id)
          .subscribe((flavor) => {
            drinks.sodaFlavor = flavor;
          });
      });
    )}
}

getTea(id: number) {
  return this.thingService.getTea(id);
}

getCoffee(id: number) {
  return this.thingService.getCoffee(id);
}

getSoda(id: number) {
  return this.thingService.getSoda(id);
}

getCoffeeTypeById(id: number) {
  return this.otherService.getCoffee(id);
}

getSodaTypeById(id: number) {
  return this.yetAnotherService.getSoda(id);
}

有关此代码的所有内容令我感到困扰。首先,它让人看不清楚,而且不清楚。接下来,这是一个大大简化的版本。实际功能约为90行。然后,它不能100%正常工作。我认为getSodaTypeById可以解决所有其他问题,因此在我需要它时不可用,因此如果我记录它,则结果为“未定义”。最后,必须有一种方法可以很清楚地完成我想做的所有事情,而且只需一个订阅即可。

存在无效的无效代码,无效的无效代码和有效的不良代码。我无法决定哪一个是最糟糕的事情。

编辑:删除“记录”并替换为“内容”

1 个答案:

答案 0 :(得分:2)

需要考虑的一些事情。尝试使用管道运算符。如果需要创建副作用,请点击。对于嵌套订阅,请尝试使用switchMap。同样,您可能不需要仅返回服务的额外方法。

这是一个快速可行的解决方案。祝一切顺利。

链接到示例https://stackblitz.com/edit/angular-rxjs-nested-subscriptions?file=src/app/app.component.ts

    this.thingService.getThingById(id).pipe(
      tap((thing) => {
        this.thing = thing;
      }),
      switchMap(_ => forkJoin([
        // NOTE: not sure where record comes from
        // if it is related to thing then it can be get passed down
        this.thingService.getTea(record.tea.id),
        this.thingService.getCoffee(record.coffee.id),
        this.thingService.getSoda(record.soda.id)
      ])),
      switchMap(([tea, coffee, soda]) => forkJoin([
        this.getCoffeeTypeById(coffee.bean.id),
        this.getSodaTypeById(soda.flavors.id)
      ]))).subscribe((data) => {
        drinks.coffeeBean = data[0];
        drinks.sodaFlavor = data[1];
      }
    );