【问题标题】:How to properly chain observables in a series of API calls如何在一系列 API 调用中正确链接 observables
【发布时间】:2019-03-18 17:03:18
【问题描述】:

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

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

// .服务

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 的方式看起来有什么问题吗?

非常感谢任何帮助!

如果第一个 activities$ Observable 没有产生任何数据,我将返回 of({}),因此我可以通过 Observable 流而无需进行不必要的 API 调用——我愿意接受有关@987654333 更流畅方式的建议@out Observable 链。

【问题讨论】:

  • 那是Observable 上极其复杂的运算符链。您的问题缺乏关于您在 Operators 中完成的几个函数调用将返回什么的实现细节。您能否考虑创建一个 StackBlitz 来复制此问题?
  • @SiddAjmera 因此我可以使用一些帮助:)。文档和其他方面给出的示例非常简单。我想我可以尝试 StackBlitz,但我不确定如何在没有真正调用的情况下复制它(我们的后端位于 Okta 之后)。那些其他函数调用都正确返回;我也可以包括它们。我希望有人能帮我发现一个明显的逻辑错误。
  • @kriskanya return observableForkJoin() 是什么?
  • @kriskanya 好的,如果问题仅在放置该行代码时出现,那么。我建议检查这些以查看它们是否正常工作。您实际的 http 调用似乎很好。 project_program_id: parseInt(projectId, 10)value: this.getProjectPhase(this.activities)。这些可能是异步抛出的,因此您看不到任何控制台输出。

标签: angular typescript rxjs reactive-programming


【解决方案1】:

原来我的同步 this.getProjectPhase(this.activities) 方法存在逻辑错误,导致应用程序进入无限循环。

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

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

【讨论】:

  • 你能用一些类似树的结构或其他简单的图表来解释一下,你追求的是什么?为什么 forkJoin 或 flatMap 不是答案?
  • 是的,我也不明白first() 的过度使用,因为http 调用无论如何只会发出一次。要么一个下一个完成,要么一个错误。
  • @AvinKavish 换句话说,下面的会更合适:observableForkJoin( this.phasesFormDataService.submitPhasesForm(this.projectId), this.pdpMetricsFormService.submitPdpForm(this.projectId), this.projectStatusFormService.submitStatusForm(this.projectId) )
  • @r2018 我们有一系列包含表单的独立组件。单击通用“保存”按钮时,我需要将所有这些表单同时提交到后端——但只有在表单已更改时才提交数据。表单数据存储在服务中,因为用户更改的表单值应该在用户浏览选项卡时保留,直到他们再次点击“保存”(或“取消”,将表单重置为 DB 值)。因此,即使未更改特定形式(我尝试通过返回 of({}) 来解决此问题,我也需要能够继续通过 Observable 链。
猜你喜欢
  • 2019-03-17
  • 1970-01-01
  • 1970-01-01
  • 2017-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-29
相关资源
最近更新 更多