JavaScript: JavaScript 非同期プログラミング

最終更新:2026-10-13

JavaScript はシングルスレッドです。一度に1つのことしかできません。しかし、ネットワークリクエストに3秒かかる場合、ページが3秒間フリーズするのは困りますよね?非同期プログラミングがこの問題を解決します。待つ代わりに番号札を受け取り、準備ができたら戻ってきます。レストランの行列システムのようなもので、キッチンのドアの前で立つのではなく、座って番号が呼ばれるのを待ちます。


関連する非同期メソッド:async/await Promise.allSettled Promise.race Promise.all setTimeout Promise catch then

1. 同期 vs 非同期

同期:コードは行ごとに実行されます。次の行は現在の行が完了するまで待ちます。列に並ぶようなもので、前の人が終わるまで待ちます。

非同期:特定の操作(ネットワークリクエスト、タイマー、ファイル I/O)は後続のコードをブロックせずに開始されます。完了時にコールバックや Promise で通知を受けます。

HTML
<script>
console.log('1');
setTimeout(function() {
  console.log('2');
}, 1000);
console.log('3');
</script>

出力順は 1 → 3 → 2 です。setTimeout は非同期で、そのコールバックは1秒後に console.log('3') をブロックせずに実行されます。



コールバックは最も基本的な非同期パターンです。関数を引数として渡し、非同期操作が完了した時に呼び出されます。

(1) サンプル:基本的なコールバックの使用

▶ 試してみよう

(2) コールバック地獄

非同期操作が順番に依存し合うと、コールバックが深くネストされます。これが悪名高い「コールバック地獄」です。

HTML
<script>
getUser(function(user) {
  getOrders(user.id, function(orders) {
    getOrderDetail(orders[0].id, function(detail) {
      getItems(detail.itemId, function(items) {
        console.log('4レベルの深さ。さらに複雑になると読みにくくなる');
      });
    });
  });
});
</script>
💡 ヒント: コールバック地獄は「水平ピラミッド」のような形で、読みにくくメンテナンスが困難です。Promise はこの問題を解決するために発明されました。



2. Promise

Promise は非同期操作の最終結果を表すオブジェクトです。3つの状態があります。

状態 説明
pending 進行中。結果はまだない
fulfilled 正常に完了。結果が利用可能
rejected 失敗。エラーが利用可能

Promise が pending から fulfilled または rejected に遷移すると、二度と元に戻りません。射た矢は召回できないようなものです。

(1) Promise の作成

HTML
<script>
const promise = new Promise(function(resolve, reject) {
  // 非同期操作
  // 成功時は resolve(result) を呼び出す
  // 失敗時は reject(error) を呼び出す
});
</script>

(2) then / catch / finally

HTML
<script>
promise
  .then(function(result) { console.log(result); })   // 成功時に実行
  .catch(function(error) { console.error(error); })    // 失敗時に実行
  .finally(function() { console.log('完了'); });       // 常に実行
</script>

(3) サンプル:Promise でリクエストをシミュレート

▶ 試してみよう

(4) チェーン

then 自体が Promise を返すため、呼び出しをチェーンできます。コールバック地獄が直線になります。

HTML
<script>
fetchUser()
  .then(function(user) { return fetchOrders(user.id); })
  .then(function(orders) { return fetchDetail(orders[0].id); })
  .then(function(detail) { console.log(detail); })
  .catch(function(err) { console.error(err); });
</script>
💡 ヒント: チェーンの最後に1つの catch を置くと、任意のステップのエラーをキャッチします。各 then の後に catch を置く必要はありません。



3. async / await

async/await は Promise のシンタシュガーです。非同期コードを同期的に見せかけ、可読性を大幅に向上させます。

(1) サンプル:上記のリクエストを async/await で書き直す

▶ 試してみよう
💡 ヒント: コールバックと比較して、async/await は「番号札をもらって待つ」を「並んでそのまま待つ」に変えます。コードフローが直線的になり、可読性が大幅に向上します。

(2) エラー処理

async/await は try/catch でエラー処理を行います。同期コードと同じパターンです。

HTML
<script>
async function loadAll() {
  try {
    const user = await fetchUser();
    const orders = await fetchOrders(user.id);
    console.log(orders);
  } catch (err) {
    console.error('何か問題がありました:', err);
  }
}
</script>


4. Promise.all と Promise.race

メソッド 説明
Promise.all([p1, p2, p3]) すべてが成功した場合のみ成功。1つが失敗すると即座に失敗
Promise.race([p1, p2, p3]) 最初に完了したもの(成功または失敗)の結果を使用

(1) サンプル:Promise.all による並行リクエスト

▶ 試してみよう
💡 ヒント: Promise.all はグループディナーのようなもの。全員が到着するまで食べられません。Promise.race はレースのようなもの。最初にゴールした人が勝ちです。独立したリクエストの高速化には all を、複数のミラーソースから最速のものを得るには race を使いましょう。


関連する非同期メソッド:async/await Promise.allSettled Promise.race Promise.all setTimeout Promise catch then

❓ よくある質問

Q 通常の関数で await は使えますか?
A いいえ。await は async 関数内でのみ使用できます。通常の関数で await を使うと構文エラーになります。トップレベルの await はモジュールでサポートされていますが、ブラウザのサポート状況は異なります。
Q Promise.all のいずれかの Promise が失敗するとどうなりますか?
A Promise.all は最初のエラーで即座に拒否されます。他の Promise はまだ実行されますが、その結果は無視されます。「結果に関係なくすべて完了する」必要がある場合は、Promise.allSettled を使いましょう。
Q async 関数は何を返しますか?
A async 関数は常に Promise を返します。return 42 としても、実際には Promise.resolve(42) を返します。そのため、.then() で結果を消費できます。

📖 まとめ

📝 練習問題

  1. 基礎:Promise + setTimeout を使って2秒の遅延をシミュレートし、成功時に「完了」と出力してください。
  2. 中級:上記を async/await で書き直し、try/catch のエラー処理を追加してください。
  3. チャレンジ:Promise.all を使って、異なる遅延(1秒/2秒/3秒)の3つのシミュレートリクエストを同時に発火し、すべて完了した時の合計経過時間を計算して表示してください。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%