【问题标题】:Subscription has an inner subscribe for each item订阅对每个项目都有一个内部订阅
【发布时间】:2025-12-25 08:10:07
【问题描述】:

我有以下服务:

public loadMatchAnalysis(clientId: string): void {
    this.logger.info(`Calling matchAnalysisService to get everything for the 'Match Analysis' page`);

    this.matchAnalysisService.getMatchAnalysis(clientId).subscribe(
      (matches: MatchAnalysis[]) => {
        matches.forEach(match => {
          forkJoin(
            this.matchAnalysisService.getMappings(clientId, match.id),
            this.matchAnalysisService.getCalculationMethods(clientId, match.id)
          ).subscribe(([mappings, calculations]) => {
            match.mappings = mappings.filter(m => m.id === match.id);
            match.children = calculations.filter(c => c.id === match.id);

            console.log('mappings', mappings);
            console.log('calculations', calculations);
            console.log('matches', matches);
          });
        });

        //THIS ONLY NEEDS TO BE CALLED ONCE
        new GetMatchAnalysisLoadedAction({ data: matches }).dispatch();
      },
      (error: HttpErrorResponse) => {
        new GetMatchAnalysisLoadedAction({ error: error }).dispatch();
      }
    );
  }

我想要完成的事情:

我需要进行 API 调用,该调用将返回 matches 的列表。每个match 都有一个id,我需要它来进行两个新的API 调用。如您所见,我正在使用forkJoin 执行调用,当我取回数据时,我正在使用我需要的数据修改matches。完成所有这些后,我需要使用所有数据 (matches) 调用 GetMatchAnalysisLoadedAction()

除了只调用一次GetMatchAnalysisLoadedAction() 之外,上面的代码大部分都可以工作——我明白为什么会这样。

是否有一些 RXJS 魔法可以很好地完成所有这些工作?

【问题讨论】:

  • 我认为您应该避免嵌套订阅,而是尝试将所有内容通过管道传递给mapswitchMap 和其他运算符,最后进行一次订阅。
  • 我想我知道该怎么做——但我不知道该怎么做并且仍然使用forEach(match of matches) 声明。
  • 我承认这并不容易。我没有精力和动力彻底解决你的案子,所以我没有给出完整的答案。我认为如果您使用map 而不是forEach(注意这与rxjs 运算符不同map,而是一个数组方法),请确保在其中的箭头函数内返回一个Observable,并且然后将所有这些包装在另一个forkJoin中,您也许可以解决它。

标签: angular rxjs rxjs6


【解决方案1】:

我是这样解决的:

  public loadMatchAnalysis(clientId: string): void {
    this.logger.info(`Calling matchAnalysisService to get everything for the 'Match Analysis' page`);

    this.matchAnalysisService
      .getMatchAnalysis(clientId)
      .pipe(
        switchMap(matches => {
          const mappings = matches.map(match => this.matchAnalysisService.getMappings(clientId, match.id));
          const calculations = matches.map(match =>
            this.matchAnalysisService.getCalculationMethods(clientId, match.id)
          );

          return forkJoin(of(matches), ...mappings, ...calculations);
        })
      )
      .subscribe(([matches, mappings, calculations]: [MatchAnalysis[], MatchAnalysisMapping[], Calculation[]]) => {
        matches.forEach(match => {
          match.mappings = mappings.filter(m => m.id === match.id);
          match.children = calculations.filter(c => c.id === match.id);
        });

        new GetMatchAnalysisLoadedAction({ data: matches }).dispatch();
      });
  }

【讨论】: