はじめに
非同期処理を書く時にPromiseを使いがちでasync/awaitを全然使えてない......かたっぱしからPromiseで書いた処理をasync/awaitに書き換えていくことでasync/await力を高めたい
環境
- typescript@3.5.3
実践
Promiseで書いている非同期処理を、async/awaitに書き換えていきます。
例1: Promiseが値を返さないパターン
Promiseが値を返さないパターン(Promise<void>
)の処理をasync/awaitを使った処理で書き直しました。
Before: Promiseで書かれた処理
Promise<void>
を返すメソッドupdateMemo()
をメソッドonSubmit()
から呼んでいます。
updateMemo()
はWebAPI(Firebase)にアクセスしてメモデータの更新処理を実施し、その実行結果をPromiseとしてonSubmit()
に返しています。
public onSubmit() { // スピナーを表示する this.spinnerService.show(); this.updateMemo(this.memo) .then(() => { // Promiseが成功したら入力フォームをリセットする this.createFormGroup.reset(); }) .finally(() => { // Promiseが成功しても失敗してもスピナーを非表示にする this.spinnerService.hide(); }); } // Promiseを返すメソッド public updateMemo(memo: Memo): Promise<void> { return this.memoCollection.doc(memo.id).update({ title: memo.title, description: memo.description, folderId: memo.folderId, updatedDate: memo.updatedDate }); }
onSubmit()
では、WebAPI(Firebase)からレスポンスが帰ってきた後の処理をthen()
の中に書いています。
After: async/awaitに書き直した処理
先ほどの処理をasync/awaitで書き直したのが下記となります。
awaitを使うことで指定した関数のPromiseの結果が返されるまで処理を待機させることができるため、このように非同期処理を逐次処理のように記述することができます。
public async onSubmit() { // スピナーを表示する this.spinnerService.show(); try { await this.memoService.updateMemo(this.memo); // Promiseが成功したら入力フォームをリセットする this.createFormGroup.reset(); } catch (err) { console.log(err); } finally { // Promiseが成功しても失敗してもスピナーを非表示にする this.spinnerService.hide(); } } // Promiseを返すメソッド public updateMemo(memo: Memo): Promise<void> { return this.memoCollection.doc(memo.id).update({ title: memo.title, description: memo.description, folderId: memo.folderId, updatedDate: memo.updatedDate }); }
Promiseを返すメソッドを呼び出す処理の前にthis.memoService.updateMemo(this.memo);
awaitを記載しています。
また、awaitはasyncがついているfunction内でしか使えないので、onSubmit()
の前にasyncを追記しました。
例2: Promiseが値を返すパターン
Promiseが値を返すパターン(Promise<firebase.firestore.DocumentReference>
)の処理をasync/awaitを使った処理で書き直しました。
Before: Promiseで書かれた処理
先ほどの例と同じような処理ですが、今回はPromiseから値を返しているので、then()
内でPromiseの返した値を受け取って処理に使っています。
public onSubmit() { // スピナーを表示する this.spinnerService.show(); this.registerMemo(this.memo) .then(docRef => { this.memoService.memoCollection.doc(docRef.id).update({ id: docRef.id }); // フォームの内容をリセットする form.resetForm(); }) .finally(() => { // スピナーを非表示にする this.spinnerService.hide(); }); } // Promiseを返すメソッド public registerMemo( memo: Memo ): Promise<firebase.firestore.DocumentReference> { return this.angularFireStore.collection('memos').add(memo); }
After: async/awaitに書き直した処理
一つ目の例と同様にPromiseを返すメソッドを呼び出す部分にawaitを記載しています(const docRef = await this.registerMemo(this.memo);
)が、それだけではなく、Promise内で返された値も受け取ってdocRef
変数に代入しています。
なお、Promise内でエラーがあった場合はcatch()
内に処理が移ります。
public async onSubmit(form: NgForm) { // スピナーを表示する this.spinnerService.show(); try { const docRef = await this.registerMemo(this.memo); this.memoCollection.doc(docRef.id).update({ id: docRef.id }); form.resetForm(); } catch (err) { console.log(err); } finally { // スピナーを非表示にする this.spinnerService.hide(); } } // Promiseを返すメソッド public registerMemo( memo: Memo ): Promise<firebase.firestore.DocumentReference> { return this.angularFireStore.collection('memos').add(memo); }