3秒でわかる
非同期処理の完了待ちを上から下へ読める形で書くJavaScriptの構文。コールバックの入れ子をなくし、例外も try/catch でそのまま扱えます。
もう少し詳しく
どういうものか
async/await は、時間のかかる処理の完了を待つコードを、同期処理と同じ見た目で書くための構文です。関数に async を付けると、その中で await が使えるようになります。await は Promise が解決するまでその行で処理を止め、結果の値を取り出します。止まっているのはその関数の中だけで、他の処理は動き続けます。
なぜ必要か
通信やファイル読み込みの完了は、いつ返ってくるか分かりません。コールバックで書くと、次の処理を関数の中の関数として書くことになり、3 段も重なると読めなくなります。async/await はこの入れ子を平らにします。加えて、コールバック方式では try/catch がエラーを捕まえられませんが、await なら通常の例外と同じ書き方で扱えます。
具体例
async function loadUser(id) {
try {
const res = await <a href="/glossary/fetch-api" class="text-primary font-medium underline underline-offset-2 hover:text-primary-dark">fetch</a>(`/api/users/${id}`);
if (!res.ok) throw new Error(`status ${res.status}`);
const user = await res.json();
return user;
} catch (e) {
console.error("取得に失敗", e);
return null;
}
}
// 独立した通信は同時に走らせる
const [a, b] = await Promise.all([loadUser(1), loadUser(2)]);上の 2 つの await は前の結果を使うので順番に待つ必要があります。一方、最後の 2 件は互いに無関係なので Promise.all でまとめると待ち時間が半分になります。
つまずきやすいところ
最大の落とし穴は、順番に待つ必要が無いものまで 1 行ずつ await してしまい、10 件の取得に 10 倍の時間をかけてしまう書き方です。もうひとつは forEach の中で await を書く間違いで、forEach は返された Promise を無視するため、待っているつもりでも全件が同時に走り、次の行へ先に進みます。順番に処理したいなら for...of を使います。await を書き忘れると Promise オブジェクトそのものが変数に入り、undefined として現れます。
覚え方
async は「この関数は途中で待つかもしれない」という宣言、await は「ここで結果が出るまで待つ」という印です。async を付けた関数は必ず Promise を返すため、呼ぶ側でも await するか .then でつなぐ必要があります。