2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScript初学者がasync/awaitで混乱する理由 - Promiseとの違いとよくある間違い

Posted at

はじめに

「async/awaitを使っているのに、なぜかundefinedが返ってくる...」
「エラーが発生しているのに、try-catchでキャッチできない...」
「コードは動いているけど、順番がおかしい...」

JavaScriptのasync/awaitを学び始めたばかりの頃、このような経験をしたことはありませんか?

async/awaitはPromiseをより読みやすく書くための構文ですが、Promiseの理解が不十分だと、思わぬところでハマってしまいます

この記事では、初心者が必ず詰まる3つのポイントと、その解決方法を実際のコード例とともに解説します。

なぜasync/awaitで混乱するのか?

async/awaitはPromiseの糖衣構文

まず、重要なことを理解しましょう。

async/awaitはPromiseの別の書き方です。 内部的にはPromiseと同じ動作をしています。

// Promiseを使った場合
function fetchUserData() {
  return fetch('/api/user')
    .then(response => response.json())
    .then(data => {
      return data;
    })
    .catch(error => {
      throw error;
    });
}

// async/awaitを使った場合(同じ動作)
async function fetchUserData() {
  try {
    const response = await fetch('/api/user');
    const data = await response.json();
    return data;
  } catch (error) {
    throw error;
  }
}

見た目は同期的なコードのように見えますが、実際には非同期処理です。この「同期的に見えるけど非同期」という点が、混乱の原因になります。

よくある間違い1: awaitを忘れる → undefinedが返ってくる

問題のコード

async function getUserName() {
  const response = fetch('/api/user'); // awaitを忘れている!
  const data = response.json(); // エラー: response.json is not a function
  return data.name;
}

const name = getUserName(); // Promiseオブジェクトが返ってくる
console.log(name); // Promise { <pending> }

なぜ動かないのか?

fetch()はPromiseを返します。awaitを付けないと、Promiseオブジェクトそのものが返ってきます

// awaitなし → Promiseオブジェクトが返る
const response = fetch('/api/user');
console.log(response); // Promise { <pending> }

// awaitあり → 実際のレスポンスが返る
const response = await fetch('/api/user');
console.log(response); // Response { ... }

解決方法

すべてのPromiseを返す処理にawaitを付けましょう。

async function getUserName() {
  const response = await fetch('/api/user'); // ✅ awaitを追加
  const data = await response.json(); // ✅ awaitを追加
  return data.name;
}

// 呼び出す側もawaitが必要
const name = await getUserName(); // ✅ awaitを追加
console.log(name); // "山田太郎"

チェックポイント

  • Promiseを返す関数(fetch, response.json()など)にawaitを付けているか?
  • 非同期関数を呼び出す側でもawaitを付けているか?

よくある間違い2: try-catchを忘れる → エラーがキャッチできない

問題のコード

async function fetchData() {
  const response = await fetch('/api/data'); // エラーが発生する可能性がある
  const data = await response.json();
  return data;
}

// エラーが発生してもキャッチされない
const data = await fetchData(); // Uncaught (in promise) Error
console.log(data); // 実行されない

なぜ動かないのか?

async/awaitを使うと、エラーはPromiseのrejectとして扱われます。try-catchがないと、エラーが上位に伝播してしまいます。

// エラーが発生した場合
async function fetchData() {
  await fetch('/api/data'); // ネットワークエラー
  // この時点でPromiseがrejectされる
  // try-catchがないと、エラーがそのまま上位に伝播
}

解決方法

必ずtry-catchでエラーハンドリングしましょう。

async function fetchData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    return data;
  } catch (error) {
    // ✅ エラーをキャッチ
    console.error('データの取得に失敗しました:', error);
    return null; // または適切なデフォルト値
  }
}

// 安全に呼び出せる
const data = await fetchData();
if (data) {
  console.log(data);
}

チェックポイント

  • 非同期処理を実行する関数にtry-catchを付けているか?
  • エラー時の処理(ログ出力、デフォルト値の返却など)を実装しているか?

よくある間違い3: 非同期関数の呼び出しを忘れる → 順番がおかしい

問題のコード

async function processData() {
  console.log('1. 開始');
  
  fetchUserData(); // awaitを忘れている!
  fetchPostData(); // awaitを忘れている!
  
  console.log('2. 完了'); // これが先に実行される
}

processData();
// 出力:
// 1. 開始
// 2. 完了  ← データ取得より先に実行される!
// (後で) ユーザーデータ
// (後で) 投稿データ

なぜ動かないのか?

awaitを付けないと、非同期処理の完了を待たずに次の処理に進んでしまいます

// awaitなし → 並列実行(順番が保証されない)
fetchUserData(); // バックグラウンドで実行
fetchPostData(); // バックグラウンドで実行
console.log('完了'); // すぐに実行される

// awaitあり → 順次実行(順番が保証される)
await fetchUserData(); // 完了を待つ
await fetchPostData(); // 完了を待つ
console.log('完了'); // 両方の処理が終わってから実行

解決方法1: 順次実行(1つずつ処理)

async function processData() {
  console.log('1. 開始');
  
  const userData = await fetchUserData(); // ✅ 完了を待つ
  const postData = await fetchPostData(); // ✅ 完了を待つ
  
  console.log('2. 完了'); // 両方の処理が終わってから実行
  return { userData, postData };
}

解決方法2: 並列実行(同時に処理)

複数の処理を同時に実行したい場合は、Promise.allを使います。

async function processData() {
  console.log('1. 開始');
  
  // ✅ Promise.allで並列実行
  const [userData, postData] = await Promise.all([
    fetchUserData(),
    fetchPostData()
  ]);
  
  console.log('2. 完了'); // 両方の処理が終わってから実行
  return { userData, postData };
}

チェックポイント

  • 非同期処理の完了を待つ必要がある場合はawaitを付けているか?
  • 複数の処理を並列実行したい場合はPromise.allを使っているか?

デバッグ方法

1. Promiseの状態を確認する

awaitを付け忘れている場合、Promiseオブジェクトが返ってきます。

const result = fetch('/api/data'); // awaitなし
console.log(result); // Promise { <pending> } ← Promiseオブジェクト

const result = await fetch('/api/data'); // awaitあり
console.log(result); // Response { ... } ← 実際のデータ

2. エラーメッセージを確認する

ブラウザのコンソールやNode.jsのエラーメッセージを確認しましょう。

// よくあるエラーメッセージ
// "response.json is not a function" → awaitを忘れている
// "Uncaught (in promise)" → try-catchを忘れている
// "undefined" → awaitを忘れている

3. 実行順序を確認する

console.logで実行順序を確認しましょう。

async function debugExample() {
  console.log('1. 開始');
  
  const data = await fetchData();
  console.log('2. データ取得完了', data);
  
  console.log('3. 終了');
}

実践例: よくあるパターン

パターン1: APIからデータを取得して表示

// ❌ 間違い
async function displayUser() {
  const response = fetch('/api/user');
  const data = response.json();
  document.getElementById('name').textContent = data.name;
}

// ✅ 正しい
async function displayUser() {
  try {
    const response = await fetch('/api/user');
    const data = await response.json();
    document.getElementById('name').textContent = data.name;
  } catch (error) {
    console.error('ユーザー情報の取得に失敗しました:', error);
    document.getElementById('name').textContent = '取得失敗';
  }
}

パターン2: 複数のAPIを順番に呼び出す

// ❌ 間違い(順番が保証されない)
async function loadData() {
  const user = fetchUser();
  const posts = fetchPosts();
  const comments = fetchComments();
  return { user, posts, comments };
}

// ✅ 正しい(順次実行)
async function loadData() {
  const user = await fetchUser();
  const posts = await fetchPosts();
  const comments = await fetchComments();
  return { user, posts, comments };
}

// ✅ 正しい(並列実行 - より効率的)
async function loadData() {
  const [user, posts, comments] = await Promise.all([
    fetchUser(),
    fetchPosts(),
    fetchComments()
  ]);
  return { user, posts, comments };
}

パターン3: エラーハンドリング

// ❌ 間違い(エラーがキャッチされない)
async function saveData(data) {
  const response = await fetch('/api/save', {
    method: 'POST',
    body: JSON.stringify(data)
  });
  return response.json();
}

// ✅ 正しい(エラーハンドリングあり)
async function saveData(data) {
  try {
    const response = await fetch('/api/save', {
      method: 'POST',
      body: JSON.stringify(data)
    });
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    return await response.json();
  } catch (error) {
    console.error('データの保存に失敗しました:', error);
    throw error; // 呼び出し元にエラーを伝播
  }
}

まとめ

async/awaitで混乱する主な原因は、Promiseの理解が不十分なことです。

覚えておくべき3つのポイント

  1. awaitを忘れない - Promiseを返す処理には必ずawaitを付ける
  2. try-catchを忘れない - エラーハンドリングを必ず実装する
  3. 実行順序を意識する - awaitがないと順番が保証されない

チェックリスト

コードを書いたら、以下のチェックリストで確認しましょう。

  • すべてのPromiseを返す処理にawaitを付けているか?
  • 非同期関数を呼び出す側でもawaitを付けているか?
  • エラーハンドリング(try-catch)を実装しているか?
  • 実行順序が正しいか?(順次実行 or 並列実行)

もっと学びたい方へ

この記事でasync/awaitの基礎を理解できた方は、次のステップとして実践的な学習を進めてみませんか?

LTechでは、JavaScriptやNode.jsの非同期処理を実際にコードを書きながら学べるカリキュラムを提供しています。

LTechで学べること

  • JavaScript基礎講座 - JavaScriptの基礎構文を段階的に学習
  • Node.js講座 - Promise、async/awaitを段階的に学習
  • Express.js講座 - 非同期処理を使ったWebアプリケーション開発
  • 実践的な演習 - コードエディタで実際に手を動かしながら学習
  • 理解度チェック - クイズで知識を定着

すべてのコースで、初心者でも理解しやすい説明実際に動かせるコード例を用意しています。

👉 LTechでJavaScriptを学ぶ


この記事が、async/awaitで困っている方の助けになれば幸いです。質問やフィードバックがあれば、コメントでお知らせください!

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?