async/await

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

このレッスンで分かること

  • async 関数は 必ず Promise を返す 関数、await で Promise の結果を待つ
  • awaitasync 関数の中 (または top-level モジュール) でのみ使える
  • 例外は try-catch で受ける、Promise の .catch と同じ役割
  • コードが同期的に書ける ─ コールバック地獄を解消する現代の標準

async/await とは

async関数とawaitキーワードで、非同期処理を同期的に書く方法を学ぼう。本レッスンでは、async/await の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。

async/await

Promiseをより直感的に書ける構文です。非同期コードが同期コードのように見えます。

基本的な使い方

JavaScript

async function fetchUser(id) { const response = await fetch(`/api/users/${id}`); const user = await response.json(); return user; }

try/catchでのエラーハンドリング

JavaScript

async function getData() { try { const data = await fetchData(); return { success: true, data }; } catch (error) { return { success: false, error: error.message }; } }

この問題では、async/awaitのエラーハンドリングパターンを同期的に練習します。タスクリストを処理し、成功/失敗を分類しましょう。

実務で遭遇するパターン

中級レッスンで学んだことが、実際の業務コードでどう登場するかを整理します。

  • API 呼び出し + DB 書き込み + レスポンス の連鎖を線形に書く
  • 並列処理は await Promise.all([p1, p2]) で同時実行
  • 順次処理は for-of + await
  • Web Scraping / バッチ処理 での非同期 I/O の連鎖

コードレビューで指摘されがちなポイント

PR を出すとシニアから入りやすい指摘です。先回りで身につけておけばレビューが一発で通ります。

  • forEachawait ─ 待たない、for-ofPromise.all(items.map(...))
  • 例外の握りつぶしtry-catch で必ず受ける、または上位で .catch
  • 不要な await ─ Promise をそのまま return すればよい場合に return await
  • 並列化できる箇所が順次になっている ─ 独立タスクは Promise.all

パフォーマンス考慮事項

  • async/await は Promise の糖衣 ─ オーバーヘッドはほぼゼロ
  • マイクロタスクキューawait の後ろはマイクロタスクとしてキューイング
  • 並列化で N 倍速い ─ 3 つの 100ms API を 300ms ではなく 100ms にできる
  • 例外スタック保持 ─ Node 12+ で改善、async 関数のスタックトレースが追いやすい
この章のポイント

ここまでの要点 async 関数は Promise を返す、await で待機、例外は try-catch。並列化は Promise.all、順次は for-of + await

よくある質問

Q. await を for ループで使うと逐次実行になりますか?

A. はい、for (const x of items) { await f(x); } は逐次実行になります。並列に走らせたいときは Promise.all(items.map(f)) を使ってください。API 呼び出しの数百件をうっかり逐次で書くと数十秒かかるので注意です。

Q. await を使わずに Promise を呼ぶとどうなりますか?

A. Promise は作られて即座に走り始めますが、戻り値はそのまま捨てられます。エラーが起きても catch されないため、上記の unhandled rejection になります。意図的に投げっぱなしにしたい場合は void promise; と書いてリンタを通すと意思表示になります。

Q. async 関数の中で同期的な処理に戻せますか?

A. 戻せません。async 関数は必ず Promise を返します。await で中身を待っても、呼び出し側からは Promise として扱う必要があります。トップレベルで使いたい場合は ESM のトップレベル await や (async () => { ... })() を使います。

次のレッスン

次は Promise.allと並列処理 で、async関数とawaitキーワードで、非同期処理を同期的に書く方法を学ぼう を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. async/await の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. async/await とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

入出力例

test-cases.txt

simulateAsyncTasks([{"data":"ok","name":"task1"},{"errorMessage":"timeout","name":"task2","shouldFail":true},{"data":"done","name":"task3"}]){"allSucceeded":false,"errorCount":1,"errors":[{"error":"timeout","name":"task2"}],"results":[{"data":"ok","name":"task1"},{"data":"done","name":"task3"}],"successCount":2} simulateAsyncTasks([{"data":1,"name":"a"},{"data":2,"name":"b"}]){"allSucceeded":true,"errorCount":0,"errors":[],"results":[{"data":1,"name":"a"},{"data":2,"name":"b"}],"successCount":2} simulateAsyncTasks([{"name":"x","shouldFail":true}]){"allSucceeded":false,"errorCount":1,"errors":[{"error":"タスクが失敗しました","name":"x"}],"results":[],"successCount":0}
main.js
main.js
学習モード

メモ

async/await

⌘S で保存