async/awaitとは(2)
Promiseを同期的に記述するasync/await
async/await構文はPromiseの仕組みを使いつつ、その見た目をガラッと変えてしまうものです。百聞は一見にしかずということで、リスト3の処理をasync/await構文で書き換えてみましょう(リスト4)。
// async functionとして定義する
async function doFetch() { // (1)
// /api/hogeのレスポンスが終わると代入が行われる
const hogeResp = await fetch("/api/hoge"); // (3)
const hogeId = translateResponse(hogeResp);
// /api/fugaのレスポンスが終わると代入が行われる
const fugaResp = await fetch(`/api/fuga?hogeId=${hogeId}`);
const fugaId = translateResponse(fugaResp);
// hogeIdもfugaIdも使える
const piyoResp = await fetch(`/api/piyo?hogeId=${hogeId}&fugaId=${fugaId}`);
const piyoId = translateResponse(piyoResp);
return piyoId;
// ここでreturnしたものがdoFetch関数が返すPromiseの結果になる
}
doFetch() // (2)
.then(piyoId => {
// piyoIdを使った処理
})
.catch(/*エラー処理*/)
一連の非同期処理同士で行われる結果の受け渡しが、doFetch関数の中で完結するようになり、見通しがよくなりました。(1)のように async キーワードを付けて関数を定義することで、Promiseによる非同期処理を定義できるようになります。この関数を実行すると、(2)のようにPromiseを返します。この関数をasync functionと呼びます。そして、このasync functionの中でのみ使えるのが、(3)で使われている await キーワードです。fetchの戻り値に対してthenを呼び出すのではなく、まるで同期的に処理を行うかのごとく、awaitを挟みつつ代入が行われています。
async functionの中では通常の関数と同じように上から下へと処理が実行されますが、 await Promiseオブジェクト を評価する時点で一旦処理が止まります(図1)。この間、他所の関数では処理が続くため、画面が止まってしまうことはありません。
その後、Promiseの結果が返ってきた時点(通常ならばthenが呼ばれるタイミングです)で、処理が再開されます(図2)。
Promiseの結果はawaitの戻り値となり、通常の値と同じように扱われます。変数に代入できるのはそのためです。これらの仕組みによって、async/awaitはPromiseを同期処理のように扱うことができます。
async/awaitのエラー処理
Promiseの結果がthenではなくawaitの戻り値として得られるのはわかりました。では、catchで得られるはずだったエラーオブジェクトは、どこからもらえるのでしょうか。ありがたいことに、エラーハンドリングの仕組みは誰もが知っているtry-catch構文でサポートされています。リスト1と同じ処理をasync/await構文で実現した場合、リスト5の通りの見た目になります。
try {
const response = await fetch("/api/hoge");
// 通信が成功した場合の処理
} catch (error) {
// エラーが発生した場合の処理
}
また、async functionの中でcatchされなかったエラーは、async functionの戻り値であるPromiseオブジェクトのcatchに渡されるようになっています(リスト4の最後の行にあるcatchがそうです)。async functionの中でtry-catchによるエラー処理を行うか、async functionを呼び出した側の処理でcatchを定義するかは、開発者の好みで使い分けてください。
[コラム]async/awaitが使える環境
async/await構文はECMAScript 2017で策定された文法です。今日では多くのブラウザに標準搭載されるようになりましたが、古いブラウザを使い続けているユーザーの手元では、そのままでは動作しません。ですが、create-react-appを使っている場合は、内部でBabel向けにasync/awaitプラグインが導入されています。JavaScriptファイルをコンパイルする際に、古いブラウザでも動く代替実装に置き換えてくれるのです。
そのため、本記事では、async/await構文について特段の設定をせずに解説を行っています。create-react-app以外の環境でasync/await構文が使えるかどうかは、都度環境を確認しながら使用してください。
