🐡

Ionic Angularで通信が失敗した時のcatchErrorを同期的に解決する

2 min read

本記事は Ionic Framework / Capacitor / Stencil Advent Calendar 2021 Advent Calendar 2021 の記事です。


通信に失敗した時にToastを表示するなど行う時、以下のコードで用足ります。

async getData(): Observable<Type> {
  return this.httpClient.get<Type>(url)
    .pipe(
      catchError((e) => {
        this.toastController.create({ message: /.../, duration: 3000 }).then(d => d.present());
        return throwError(() => e);
      })
    )
}

しかしながら、実務でのコードだとToastでさくっと表示をだすのではなく、アラートでエラーを表示したい場合があります。以下みたいな感じですね。

async getData(): Observable<Type> {
  return this.httpClient.get<Type>(url)
    .pipe(
      catchError((e) => {
        this.alertController.create({
          header: 'エラー',
          message: 'エラー内容はなになにです',
          buttons: [
            {
              text: '閉じる',
            },
          ],
        }).then(d => d.present());
        return throwError(() => e);
      })
    )
}

ただこれはひとつ大きな問題をもっています。それは、「アラートは非同期で処理される」ということです。具体的には、アラートの閉じるをクリックするよりも先にエラーがスローされるので処理が完了してしまいます。なのでたとえば以下のようにページコンポーネントでコードを書いた場合問題があります。

getData().subscribe({
  next: () => { /../ },
  error: () => {
    // 失敗したからモーダルを閉じる
    this.modalController.dismiss();
  },
})

このコードを実行した場合、アラートを閉じる前にエラーがスローされるため、モーダルが閉じられてしまいます。このように「エラーを同期的に処理したい場合」は、上記ではなく以下のように書くと解決します。

async getData(): Observable<Type> {
  return this.httpClient.get<Type>(url)
    .pipe(
      catchError(error => from(new Promise(async (resolve, reject) => {
        const alert = await this.alertController.create({
          header: 'エラー',
          message: 'エラー内容はなになにです',
          buttons: [
            {
              text: '閉じる',
              handler: () => reject(error),
            },
          ],
        });
        return await alert.present();
      }).then(() => undefined))),
    )
}

catchErrorをObservableで受け取るために、 from でPromiseを変換します。Promiseの中は自由に書けるのですが、1つ目は「エラーハンドリングなので、rejectする」ことに気をつけてください。実際、handler内でrejectしています。 2つ目は、Promise自体をthenしておく必要があります。これが行われていない場合、型が unknown になってしまい、型を正常にハンドリングできなくなります。

こちら、 日本Angularユーザグループのlacoさんに助言いただきました。 。ありがとうございました!!

それではまた。

Discussion

ログインするとコメントできます