🐡
Ionic Angularで通信が失敗した時のcatchErrorを同期的に解決する
本記事は 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