5. Async/Await

async/await構文の基本概念と使い方を学びます。Promiseをより読みやすく書く方法、エラーハンドリング、並列処理の実装方法を実践的に学習し、現代的なJavaScriptの非同期処理をマスターします。

この章で学べること

  • async/awaitとは何かを理解する
  • Promiseとasync/awaitの違いを学ぶ
  • async関数とawaitキーワードの使い方を習得する
  • try-catch文を使ったエラーハンドリングを学ぶ
  • awaitを使ってはいけない場面を理解する
  • 実践的な演習を通じてasync/awaitを習得する

Async/Awaitとは?

async/awaitは、Promiseをより読みやすく書くための構文です。

Promiseのthen/catchを、まるで同期的なコードのように書くことができます。これにより、非同期処理のコードがより理解しやすくなります。

従来のPromiseとAsync/Awaitの比較

まず、同じ処理をPromiseとasync/awaitで書いた例を見てみましょう。

Promiseを使った場合
function fetchUserData() {
  return fetch('/api/user')
    .then(response => response.json())
    .then(data => {
      return data;
    })
    .catch(error => {
      console.error('エラー:', error);
    });
}
async/awaitを使った場合
async function fetchUserData() {
  try {
    const response = await fetch('/api/user');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('エラー:', error);
  }
}

比較のポイント

  • 可読性: async/awaitの方が同期的なコードのように読みやすい
  • エラーハンドリング: try-catch文で一括してエラーを処理できる
  • ネスト: 深いネストを避けることができる

Async/Awaitの基本構文

async関数の宣言

async/awaitを使うには、まず関数をasyncキーワードで宣言する必要があります。

async function myAsyncFunction() {
  // 非同期処理をここに書く
}

awaitキーワード

awaitキーワードは、Promiseが完了するまで待機します。awaitはasync関数内でのみ使用できます

async function example() {
  const result = await somePromise;
  console.log(result);
}

基本的な例

以下は、async/awaitを使った基本的なサンプルコードです。

// Promiseを返す関数
function delay(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

// async/awaitを使った関数
async function example() {
  console.log('開始');
  
  await delay(1000); // 1秒待機
  console.log('1秒後');
  
  await delay(1000); // さらに1秒待機
  console.log('2秒後');
  
  console.log('完了');
}

example();

実行結果

開始 1秒後 2秒後 完了

エラーハンドリング

try-catch文を使ったエラーハンドリング

async/awaitでは、try-catch文を使ってエラーハンドリングを行います。さらに、finallyブロックを使用することで、成功・失敗に関わらず必ず実行される処理を記述できます。

async function fetchData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    return data;
  } catch (error) {
    // エラーハンドリングを行う
    console.error('データの取得に失敗しました:', error);
  } finally {
    // 成功・失敗に関わらず実行される処理
    console.log('データ取得処理が完了しました');
  }
}

awaitを使ってはいけない場面

便利なawaitですが、使い方次第ではパフォーマンスが低下する場合があります。

ループ内でのawaitの問題

ループ内でawaitを使うと、処理が順次実行されてしまい、並列処理のメリットが失われます。

❌ 悪い例:ループ内でawait
const urls = ["a.json", "b.json", "c.json"];

async function fetchAll() {
  for (const url of urls) {
    const res = await fetch(url); // ←ここで毎回待ってしまう
    const data = await res.json();
    console.log(data);
  }
}

この例では、各URLを順番に処理するため、3つのリクエストが並列実行されません。

✅ 良い例:並列で実行
const urls = ["a.json", "b.json", "c.json"];

async function fetchAll() {
  const promises = urls.map(url => fetch(url)); // まとめてPromise作成
  const responses = await Promise.all(promises); // 全部終わるのを待つ

  const datas = await Promise.all(responses.map(r => r.json()));
  console.log(datas);
}

この例では、すべてのリクエストが並列実行され、効率的です。

注意点

  • ループ内のawait: 処理が順次実行され、パフォーマンスが低下
  • Promise.all(): 複数の非同期処理を並列実行
  • 適切な使い分け: 順次実行が必要な場合のみループ内でawaitを使用

実践演習

演習①:基本的なasync/await

以下の要件を満たすasync関数を作成してください。

  • 1秒後に「Hello」を表示
  • さらに1秒後に「World」を表示
  • 最後に「完了」を表示
// ヒント:delay関数を使用
function delay(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

// ここにasync関数を書いてください

実践してみよう

上記のコードを実際に書いて実行してみましょう。コードエディタで練習できます。

コードエディタで練習する

演習②:エラーハンドリング

以下の要件を満たすasync関数を作成してください。

  • ランダムで成功または失敗する処理を実行
  • 成功時は「処理が成功しました」を表示
  • 失敗時は「処理が失敗しました」を表示
// ランダムで成功または失敗する関数
function randomProcess() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (Math.random() > 0.5) {
        resolve('成功');
      } else {
        reject('失敗');
      }
    }, 1000);
  });
}

// ここにasync関数を書いてください

まとめ

async/awaitは、Promiseをより読みやすく書くための強力な構文です。以下のポイントを理解しましょう。

  1. async関数: 非同期処理を含む関数を宣言
  2. awaitキーワード: Promiseの完了を待機
  3. try-catch文: エラーハンドリング
  4. 可読性の向上: 同期的なコードのように書ける
  5. ループ内のawait: 並列処理を阻害するため注意が必要
  6. Promise.all(): 複数の非同期処理を効率的に並列実行

async/awaitを理解することで、非同期処理をより効率的に扱えるようになります。これでNode.jsの基本的な非同期処理の学習は完了です。

次回予告

次回はNode.jsのファイル操作について学びます。