为什么 RxJs 在链式 http 调用完成之前完成执行?

Why does RxJs finalize execute before chained http calls are complete?

Angular 11 个站点。我已经搜索了所有地方,但我仍在为这似乎是一个很常见的场景而苦苦挣扎。

目标: 执行一个 http 调用以检索项目列表,然后执行另一个 http 调用以检索第一个项目的详细信息。两次调用完成后(无论成功还是失败),隐藏组件的整体等待指示器。

这是一个高度简化的版本,没有空检查和错误处理:

  ngOnInit(): void {
    this.getPackages();
  }

  private getPackages() {
    this._packageSubscription = this.packageService.getPackages()
      .pipe(
        finalize(() => {
          console.log('getPackages finalize');
          this.loadingPackages = false; //hide wait indicator: happens BEFORE getPackageDetails is finished
        })
      )
      .subscribe(
        packages => {
          this.packageOptions = packages;
          this.selectedPackage = packages[0];
          this.loadSelectedPackage();
        }
      );
  }

  public loadSelectedPackage() {
    this._packageDetailsSubscription = this.packageService.getPackageDetails(this.selectedPackage!.Id)
      .pipe(
        finalize(() => {
          console.log('getPackageDetails finalize');
        })
      )
      .subscribe(
        packageDetails => {
          this.packageDetails = packageDetails;
        }
      );
  }

执行时,控制台显示:

getPackages finalize
getPackageDetails finalize

我希望相反。

我知道我可以添加我的代码来为 getPackageDetails() 停用内部 finalize() 中的等待指示器,但是这有一些问题:

  1. 这似乎是错误的。为什么我不知道链在外层何时完成?
  2. 那只能涵盖一切都成功的情况。当第一次调用失败时,我必须在某处添加第二行代码以隐藏等待指示器。
  3. loadSelectedPackage() 可以在用户操作更改所选包后再次调用,因此处理仅与初始加载相关的内容更没有意义。

那么为什么我的外部 finalize() 不等到链完成?或者我还能如何知道链何时完成?

这实际上是您得到的正确结果。您编写代码的方式没有链条。这两个订阅是相互独立的,通过从 subscribe 中调用 this.loadSelectedPackage(); 你不会连接它们或类似的东西。

您实际上应该使用一些地图运算符并且只订阅一次。在你的情况下,我认为 concatMap() 是合适的。让 getPackages() 发生,将值分配给对象属性,然后连接新的可观察对象以获取详细信息。

private getPackages() {
  this._packageSubscription = this.packageService.getPackages()
    .pipe(
      concatMap(packages => {
        this.packageOptions = packages;
        this.selectedPackage = packages[0];
        return this.packageService.getDetails(this.selectedPackage!.Id);
      }),
      finalize(() => {
        console.log('getPackages finalize');
        this.loadingPackages = false; 
      })
    )
    .subscribe(packageDetails => {
      this.packageDetails = packageDetails;
    });
}

这应该可以解决问题。