首页 > 解决方案 > 等待所有可观察对象完成(按顺序 + 父子关系)

问题描述

我对 Angular 和 Observables 有疑问,我在这个 Stackblizt 中复制了它:https ://stackblitz.com/edit/angular-ivy-dl1y3y

放一些上下文:

好的,让我一步一步地告诉你。

步骤1

第一步是调用第一个 URL ( assets/step-1-getAccountReference.json) 来检索帐户参考 ID:

{
  "accountIdRef": "/assets/step-2.getAccount.json"
}

第2步

有了这个accountIdRef,我可以调用另一个 URL ( "/assets/step-2.getAccount.json") 来检索帐户信息:

{
   "accountId": "123",
   "details": [
      {
         "nameRef": "/assets/step-3-pet-1-name.json",
         "genderRef": "/assets/step-3-pet-1-gender.json"
      },
      {
         "nameRef": "/assets/step-3-pet-2-name.json",
         "genderRef": "/assets/step-3-pet-2-gender.json"
      }
   ]
}

第 3 步

最后一步是for each通过调用其他一些 url (nameRefgenderRef) 来检索所有宠物的详细信息。

如果您打开控制台,您应该会看到,如果我直接订阅并登录帐户,则会显示此信息(步骤 1步骤 2中的 Observables已完成):

{
    "accountId": "123",
    "details": [
        {
            "nameRef": "/assets/step-3-pet-1-name.json",
            "genderRef": "/assets/step-3-pet-1-gender.json"
        },
        {
            "nameRef": "/assets/step-3-pet-2-name.json",
            "genderRef": "/assets/step-3-pet-2-gender.json"
        }
    ]
}

如果我在 3 秒后再次登录该帐户,则会显示此信息(步骤 3中的所有 observables都已完成):

{
    "accountId": "123",
    "details": [
        {
            "nameRef": "/assets/step-3-pet-1-name.json",
            "genderRef": "/assets/step-3-pet-1-gender.json",
            "name": "Santa's Little Helper",
            "gender": "Male"
        },
        {
            "nameRef": "/assets/step-3-pet-2-name.json",
            "genderRef": "/assets/step-3-pet-2-gender.json",
            "name": "Snowball II",
            "gender": "Female"
        }
    ]
}

我想等待所有 observables 完成(包括第 3 步),但当然是动态的,而不是使用固定的超时。

这是我现在所拥有的:

export class HttpService {
  constructor(private http: HttpClient) {}

  getAccount(): Observable<Account> {
    return this.http.get("assets/step-1-getAccountReference.json").pipe( // Step 1
      mergeMap((accountReference: AccountReference) => {
        return this.http.get("" + accountReference.accountIdRef);        // Step 2
      }),
      delay(500),
      map((account: Account) => {
        account.details.forEach((details: AccountDetails) => {           // Step 3
          let name$ = this.http.get("" + details.nameRef);
          let gender$ = this.http.get("" + details.genderRef);
          forkJoin([name$, gender$]).subscribe(results => {
            details.name = results[0]["name"];
            details.gender = results[1]["gender"];
          });
        });

        return account;
      })
    );
  }
}

那么,我怎样才能调整这段代码,使第 3 步是同步的?我应该使用哪个运算符来替换它forEach

谢谢您的帮助 !

标签: angularrxjsobservable

解决方案


您可以做的是映射您的详细信息并构建一个可观察的数组来填充缺失的属性。
然后将该数组传递给 forkJoin,它将丢失的数据提取到您的详细信息中。最后,您更新您的帐户详细信息并返回帐户。

export class HttpService {
  constructor(private http: HttpClient) {}

  getAccount(): Observable<Account> {
    return this.http.get("assets/step-1-getAccountReference.json").pipe( // Step 1
      mergeMap((accountReference: AccountReference) => {
        return this.http.get("" + accountReference.accountIdRef);        // Step 2
      }),
      delay(500), // why this delay ?
      mergeMap((account: Account) => {
        const populatedDetailsObservableArray = account.details.map((details: AccountDetails) => {
            return forkJoin([name$, gender$]).pipe(
                map(results => {
                    details.name = results[0]["name"];
                    details.gender = results[1]["gender"];
                    return details;
                })
            );
        });
        return forkJoin(populatedDetailsObservableArray).pipe(
            map((newDetails: AccountDetails[]) => {
                account.details = newDetails;
                return account;
            })
        );
      })
    );
  }
}

推荐阅读