スポンサーリンク

subscribeでネストが深くなるのと解決する方法[Angular・rxjs]

ネスト(入れ子)が深くなる

Angularアプリの開発で、非同期処理を以下のように、連鎖して使うようなケースがあると、ネストが深くなることがあるかと思います。

  1. AのデータをAPIリクエストで取得
  2. Bのデータを、Aのデータを使ってAPIリクエストで取得
  3. 上記の連鎖するような処理を繰り返して、ネストが深くなる。

コードだと以下のような形ですね。

this.aService.getData().subscribe(aData => {
  console.log(aData['id']);
  this.bService.getData(aData['id']).subscribe(bData => {
    console.log(bData['id']);
    this.cService.getData(bData['id']).subscribe(cData => {
      console.log(cData['id']);
    })
  })
})

このネストが深くなって、可読性が悪くなるのを解決する方法について、検討していきます。

解決方法

switchMapを使う

switchMapを使えば、以下のようにネストを深くせずに、
連鎖してAPIをリクエストするような処理を記述できます。

this.aService.getData().pipe(
  switchMap(dataA => this.bService.getData(dataA['id'])),
  switchMap(dataB => this.cService.getData(dataB['id']))
).subscribe(dataC => {
  console.log(dataC);
})

forkJoinを使う

各非同期処理が、連鎖的に処理するようなものでなければ、
forkJoinで並列処理して、全ての結果が返ってから、subscribe内の処理をするといったこともができます。

forkJoin({
  dataA: this.aService.getData(),
  dataB: this.bService.getData(),
  dataC: this.cService.getData()
}).subscribe(({ dataA, dataB, dataC}) => {
  console.log(dataA, dataB, dataC);
})

async/await + firstValueFrom

async/await + firstValueFromを使って、Promiseに変換することで、
subscribeを使わないようにすることで、ネストが深くなるのを回避できます。

ただ、Observableでなくなるため、Observable特有の複雑なリアクティブ処理をしたいといったケースの場合は不向きです。

  async ngOnInit(): Promise<void> {
    const aData = await firstValueFrom(this.aService.getData());
    const bData = await firstValueFrom(this.bService.getData());
    const cData = await firstValueFrom(this.cService.getData());

    console.log(aData, bData, cData);
  }

まとめ

パターンおすすめの方法
順次に非同期処理をしたいswitchMap
並列に実行し、全て終わったら処理したいforkJoin
最小限の処理で簡単に書きたいasync/await + firstValueFrom