如何在一系列 API 调用中正确链接可观察对象

How to properly chain observables in a series of API calls

提问人:kriskanya 提问时间:10/14/2018 最后编辑:kriskanya 更新时间:10/14/2018 访问量:835

问:

我有几个相互依赖的 API 调用。具体来说,我无法让最终的 Observable 正确返回:它会导致应用程序无限期滞后。

如果我自己调用它,然后调用它,它似乎工作正常。这表明这是我在前端的 Observable 链接的问题。就目前而言,该方法甚至没有在后端调用(我设置了一个断点)。this.projectAttributeService.findAndUpdateByProjectAndMetumId({...}).subscribe

。服务

submitPhasesForm(projectId) {
  return this.activityDateService.activities$.pipe(
    first(),
    concatMap((activities: ActivityDate[]) => {
      this.activities = activities;
      if (this.activities.length === 0) {
        return observableOf({});
      }
      this.activities = activities.map(a => {
        a.project_program_id = parseInt(projectId, 10);
        return a;
      });
      return this.activityDateService.update(this.activities);
    }),
    mergeMap(() => {
      if (this.activities.length === 0) {
        return observableOf({});
      }
      return this.projectAttributeService.getAllMetadata(3).pipe(first())
    }),
    mergeMap((metaData: ProjectAttMetadataAPIResponse) => {
      if (this.activities.length === 0) {
        return observableOf({});
      }
      const metaDataId = (metaData as any).find(m => m.name === 'Phase').id;

    // EDIT: the problem ended up being with the synchronous 
    // this.getProjectPhase(this.activities) method below
      return this.projectAttributeService.findAndUpdateByProjectAndMetumId({
        project_program_id: parseInt(projectId, 10),
        value: this.getProjectPhase(this.activities),
        project_attrib_metum_id: metaDataId
      })
    })
  )
}

这是看起来像(调用似乎可以自行工作):findAndUpdateByProjectAndMetumId()

findAndUpdateByProjectAndMetumId(body: ProjectAttribute): Observable < ProjectAttribute > {
  return this.http.put < ProjectAttribute > (`${ environment.API_URL }project-attribute`, body);
}

这就是被称为的地方:submitPhasesForm()

。元件

import { forkJoin as observableForkJoin } from 'rxjs';

return this.projectService.patch(this.projectId, {
    summary: projectSummary || proj.summary
  }).pipe(
    first(),
    mergeMap(() => {
      return observableForkJoin(
        this.phasesFormDataService.submitPhasesForm(this.projectId).pipe(first()),
        this.pdpMetricsFormService.submitPdpForm(this.projectId).pipe(first()),
        this.projectStatusFormService.submitStatusForm(this.projectId).pipe(first())
      )
    })
  )
  .subscribe((res) => {
    this.router.navigate([`./pdp/${this.currentTab}/${this.projectId}`]);
  });

其他两个调用非常相似,但更短:

submitPdpForm(projectId) {
    return this.pdpMetricsForm$.pipe(
      first(),
      concatMap((formGroup: FormGroup) => {
        if (!formGroup.get('etRadioModel')) {
          return observableOf({});
        }

        const objSend = {...}
        return this.projectService.upsertPdpMetrics(projectId, objSend);
      })
    )
  }

...

submitStatusForm(projectId) {
    return this.metrics$.pipe(
      first(),
      tap(metrics => {
        this.metricsData = metrics;
      }),
      mergeMap(() => this.statusesForm$),
      observableMap(statusesForm => {
        const formGroup = statusesForm;

        if (!formGroup.get('resourceRationale')) {
          return {};
        }

        const obj = [{...}]

        return sendObj;
      }),
      mergeMap((sendObj: any) => {
        if (isEmpty(sendObj)) { return observableOf(sendObj) };
        return this.projectService.upsertMetrics(projectId, sendObj).pipe(first());
      })
    )

我链接或调用这些 Observable 的方式有什么不对劲吗?

任何帮助都非常感谢!

如果第一个 Observable 没有产生任何数据,我将返回,这样我就可以在不进行不必要的 API 调用的情况下通过 Observable 流---我愿意接受关于以更时尚的方式退出 Observable 链的建议。of({})activities$break

Angular TypeScript RXJS 反应式编程

评论

0赞 SiddAjmera 10/14/2018
THAT,是一个极其复杂的运算符链。您的问题缺少有关您在 Operator 中执行的几个函数调用将返回什么的实现详细信息。您能否考虑创建一个 StackBlitz 来复制此问题?Observable
1赞 FAISAL 10/14/2018
stackoverflow.com/a/52317734/1791913
0赞 kriskanya 10/14/2018
@SiddAjmera 因此,为什么我可以使用一些帮助:)。文档和其他方面给出的示例非常微不足道。我想我可以尝试 StackBlitz,尽管我不确定如何在没有实际调用的情况下复制它(我们的后端在 Okta 后面)。这些其他函数调用都正确返回;我也可以把它们包括在内。我希望有人能帮我发现一个明显的逻辑错误。
0赞 Avin Kavish 10/14/2018
@kriskanya什么是?return observableForkJoin()
1赞 Avin Kavish 10/14/2018
@kriskanya 好的,如果问题仅在放置该行代码时发生,那么。我建议检查这些,看看它们是否正常工作。您的实际 http 调用似乎没问题。, .这些可能是异步抛出的,因此你看不到任何控制台输出。project_program_id: parseInt(projectId, 10)value: this.getProjectPhase(this.activities)

答:

0赞 kriskanya 10/14/2018 #1

事实证明,我的同步方法有一个逻辑错误,导致应用程序进入无限循环。this.getProjectPhase(this.activities)

否则,Observable 运算符工作正常。

如果是空的,我仍然想找出一种更时尚的方法来突破该流。this.activities

评论

0赞 r2018 10/14/2018
你能用一些树状结构或其他简单的图表来解释一下,你在追求什么吗?为什么 forkJoin 或 flatMap 不是答案?
0赞 Avin Kavish 10/14/2018
是的,我不明白两者的过度使用,因为 http 调用无论如何只发出一次。要么是下一个,要么是一个完成,要么是一个错误。first()
0赞 kriskanya 10/16/2018
@AvinKavish 换言之,以下各项更为合适:observableForkJoin( this.phasesFormDataService.submitPhasesForm(this.projectId), this.pdpMetricsFormService.submitPdpForm(this.projectId), this.projectStatusFormService.submitStatusForm(this.projectId) )
0赞 kriskanya 10/16/2018
@r2018 我们有一系列包含表单的独立组件。当单击通用保存按钮时,我需要同时将所有这些表单提交到后端---但只有在表单被更改时才提交数据。表单数据存储在服务中,因为用户更改的表单值应在用户跨选项卡导航时保留,直到他们再次点击保存(或取消,这会将表单重置为数据库值)。因此,即使特定形式尚未更改,我也需要能够继续通过 Observable 链(我试图通过返回 .of({})