首页 > 解决方案 > 如何使用 rxjs 运算符和转换链接多个依赖订阅?

问题描述

我有一个函数应该从一个 api 中提取数据,然后使用结果从几个额外的 api 中获取数据。第一次数据拉取对所有这些都是必要的,但一些后续调用可以并行运行,而其他调用则需要一个或多个并行调用返回的数据才能运行。目前,我通过在另一个订阅中进行三个订阅来完成其中的一些工作(对于那些正在计数的人来说,这是一个函数中的四个订阅),但这对我来说是错误的,我读过的所有内容都说它是要么是反模式,要么是完全错误的。我需要一种方法来让所有这些事情以正确的顺序运行——在需要顺序的地方——并返回我的数据。

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

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 正在解决其他所有问题,因此在我需要它时不可用,所以如果我记录它的结果是“未定义”。最后,必须有某种方法来完成我想做的所有事情,并且只需一个订阅即可。

有有效的坏代码,无效的坏代码,以及有效的坏代码。我无法决定哪一个是最糟糕的事情。

编辑:删除“记录”并替换为“事物”

标签: angularrxjs

解决方案


有些事情要考虑。尝试使用管道运算符。如果您需要创建副作用,请使用点击。对于嵌套订阅,请尝试 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];
      }
    );

推荐阅读