9cubed
ブログ | PHP | JavaScript | TypeScript | Vue.js | Laravel | Tailwind | Vite | Python | MariaDB | SQLite | Node.js | Linux | PowerShell | Docker | Git | Web | その他
次の記事 >

async、await

JavaScript

[JavaScript]Promise について

公開日:2026-03-19
更新日:2026-03-19

1. 概要

Promise の使い方についてです。Promise は、非同期処理を扱う際に使用します。

2. サンプル1-基本

2.1 コード

コード
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Promise</title>
    <script>
      new Promise((resolve, reject) => {

        setTimeout(() => {
          // 非同期処理。1秒後に実行される

          if (true) {
            // 正常
            resolve("OK");           // 現在の関数の終了後、then に渡した関数が実行される
          } else {
            // エラー
            reject(new Error("NG")); // 現在の関数の終了後、catch に渡した関数が実行される
          }
        }, 1000);

      })
      .then((result) => {
        console.log(result); // OK
      })
      .catch((error) => {
        console.log(error);
      });
    </script>
  </head>
  <body>
    Promise
  </body>
</html>

実行結果
コード
OK

2.2 説明

Promise の生成時に、非同期処理を行う関数を渡します。
非同期処理が正常終了した場合、resolve() を呼ぶと、then() に指定した関数が実行されます。
エラーが発生した場合、reject() を呼ぶと、catch() に指定した関数が実行されます。

3. サンプル2-処理の流れ

サンプル1に、処理の流れがわかるようにするためのログ出力を追加しました。

3.1 コード

コード
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Promise</title>
    <script>
      new Promise((resolve, reject) => {
        console.log("1"); // すぐに実行される

        setTimeout(() => {
          // 非同期処理。1秒後に実行される
          console.log("3");

          if (true) {
            // 正常
            resolve("OK"); // 現在の関数の終了後、then に渡した関数が実行される
          } else {
            // エラー
            reject(new Error("NG")); // 現在の関数の終了後、catch に渡した関数が実行される
          }

          console.log("4"); // then() や catch() に渡した関数の前に実行される
        }, 1000);
      })
      .then((result) => {
        // result には resolve() に渡された値が入っている
        console.log(`5:${result}`); // 5:OK
      })
      .catch((error) => {
        // error には reject() に渡された値が入っている
        console.log("6");
        console.log(error);
      });

      console.log("2");
    </script>
  </head>
  <body>
    Promise
  </body>
</html>

実行結果
コード
1
2
3
4
5:OK

3.2 説明

new Promise() に渡した関数は、同期的にすぐに実行されます。
then() と catch() は、引数の関数の保持だけ行い、まだ関数は実行しません。

そのため、最後の console.log("2") が2番目に出力されています。

その後、1秒後に setTimeout() に渡した関数が実行されます。
この関数の中で resolve() を呼んでいますが、これにより、then() に渡した関数が実行されます。
但し、resolve() の中で直接 then() の関数を実行しているのではなく、現在の関数を抜けてから別のタイミングで実行されます。
そのため、console.log("4") が then() よりも先に実行されています。

4. サンプル3-then と catch を繋げる

4.1 コード

コード
      new Promise((resolve, reject) => {
        resolve("OK");
      })
      .then((result) => {
        console.log(1);
      })
      .then((result) => {
        console.log(2);
      })
      .then((result) => {
        console.log(3);
        throw new Error("error"); // エラーをスローする
      })
      .then((result) => {
        console.log(4);           // 実行されない。上記エラーで rejected のため。
      })
      .then((result) => {
        console.log(5);           // 実行されない。上記エラーで rejected のため。
      })
      .catch((error) => {
        console.log("6:Error");   // エラーをキャッチして対処したため、fulfilled になる
      })
      .then((result) => {
        console.log(7);           // 実行される。fulfilled のため。
      })
      .then((result) => {
        console.log(8);           // 実行される。fulfilled のため。
      })
      .catch((error) => {
        console.log("9:Error");   // 実行されない。fulfilled のため。
      });

実行結果
コード
1
2
3
6:Error
7
8

4.2 説明

【注意】次の説明は、実行の流れを把握するためのイメージです。実際の仕組みとは異なります。

.then() や .catch() は、繋げて書くことができます。

then() に指定した関数は、Promise の状態が fulfilled(成功)の時に実行され、
catch() に指定した関数は、Promise の状態が rejected(失敗)の時に実行されます。

状態が fulfilled になるのは、resolve() を実行した時や、then() や catch() の関数が正常終了した場合です。
状態が rejected になるのは、reject() を実行した時や、then() や catch() の関数で例外をスローした場合です。

ちなみに、then() や catch() の関数の中で、
さらに非同期処理を行うために Promise を生成して、その Promise を return した場合は、
その Promise に対応する resolve() または reject() を実行した方の状態になります。

5. サンプル4-then() に2つの関数を指定

then() に、fulfilled になった時に呼ばれる関数と、rejected になった時に呼ばれる関数を指定しています。
これにより、catch() を使わなくても、rejected になった時の処理を書くことができます。

5.1 コード

コード
      new Promise((resolve, reject) => {
        reject("NG");
      })
      .then(
        (result) => {
          console.log(result);
        },
        (error) => {
          console.log(error);
        },
      )
      .catch((error) => {
        console.log(error); // ここは実行されない。then() の rejected になった時の関数でエラーを対処しているため。
      });

上記のコードの最後の catch() の関数は、then() の 2番目の引数の関数で正常終了しているため、実行されません。
then() に渡した関数の中で throw new Error("error"); すると、実行されます。

また、then() は2つの関数を受け取れますが、catch() は rejected 用の関数1つだけです。

6. サンプル5-then() の中で非同期処理

6.1 コード

コード
      new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve("OK");
        }, 1000);
      })
      .then((result) => {
        console.log("1秒経過");

        const promise = new Promise((resolve, reject) => {
          setTimeout(() => {
            reject("NG");
          }, 1000);
        });

        return promise;
      })
      .then((result) => {
        console.log("then()"); // 実行されない
      })
      .catch((error) => {
        console.log("Error"); // 実行される
      });

実行結果
コード
1秒経過
Error

6.2 説明

then() の関数の中で、さらに非同期処理を実行することができます。
この場合、Promise を生成して、その Promise を return します。
そうすると、元の Promise の状態は、return された Promise の状態を引き継ぐようなイメージになります。
そして、生成した Promise の reject() を呼ぶと、状態が rejected となり、後の catch() の関数が実行されるようになります。

7. イベントループ、マクロタスク、マイクロタスク

JavaScript は、イベントループと言うループがあり、
そこで、マクロタスクキューとマイクロタスクキューのタスクを実行しています。

イベントループのイメージ
コード
while (true) {
  if (マクロタスクキューにタスクがあるか?) {
    マクロタスクキューからタスクを1件取得
    タスクの実行
  }
  
  while (マイクロタスクキューにタスクがあるか?) {
    マイクロタスクキューからタスクを1件取得
    タスクの実行
  }

  レンダリング
}

マクロタスクは、1回のイベントループで1つだけ実行します。
マイクロタスクキューは、キューが空になるまで実行します。
そのため、マイクロタスクキューにタスクがある限り、後ろのレンダリング処理が実行されないため、画面がフリーズします。

マクロタスク
・setTimeout
・setInterval
・DOMイベント(click、keydown)
・I/Oコールバック(ファイル入出力など)
など

マイクロタスク
・Promise.then()、Promise..catch()、 Promise.finally()
・queueMicrotask
など

余談:setInterval() は一定間隔でマクロタスクキューにタスクを追加する際に、前回追加した処理が終了したかどうかは見ていません。
そのため、重い処理をする場合は、マクロタスクキューがずっと溜まっていく可能性があります。
その場合は、setTimeout() を使い、タスクの最後に setTimeout() を呼ぶことで、安全に一定間隔で処理させることができます。

8. script タグの JavaScript のコード

script タグの JavaScript のコードは、マクロタスクキューに追加されて処理されます。
そのため、script タグを分けると、動作が変わります。

コード
    <script>
      console.log("1");
      
      // マイクロタスクキューに関数を追加
      queueMicrotask(() => {
        console.log("microtask");
      });

      console.log("2");
    </script>
    <script>
      console.log("3");
    </script>
実行結果
1
2
microtask
3
script タグが2つあるため、マクロタスクが2つになります。
そのため、2つ目のマクロタスクが実行される前に、マイクロタスクが実行されます。

9. Promise の仕組み

9.1 then() と catch() の戻り値

これまでのサンプルでは、then() や catch() をメソッドチェーンで書いてきましたが、
これは then() と catch() が、中で Promise を生成して返しているためです。

そのため、then() と catch() の戻り値(Promise)を取得して、メソッドチェーンを使わずに書くこともできます。

コード
      const promise1 = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve("OK");
        }, 1000);
      });
      
      const promise2 = promise1.then((result) => {
        console.log(result);
      });
      
      const promise3 = promise2.catch((error) => {
        console.log(error);
      });
      
      if (promise1 !== promise2) console.log("異なる");
      if (promise1 !== promise3) console.log("異なる");

then() と catch() は新しい Promise を生成しているため、上記の promise1、promise2、promise3 は、全て異なるオブジェクトとなります。

9.2 Promise のコンストラクタ、resolve()、reject()

コード
new Promise( (resolve, reject) => {  } );
Promise は、resolve() と reject() を内部で保持しています。
Promise のコンストラクタに関数を渡すと、引数に resolve と reject を入れてすぐにコールバックしてくれます。

resolve() を実行すると、then() で保持した関数を実行してくれます。
reject() を実行すると、catch() で保持した関数を実行してくれます。

9.3 then()

then() は、resolve() が呼ばれた時に実行する関数を Promise に保持します。
この時に、引数の関数をそのまま保持するのではなく、
関数実行後に、then() 内部で生成した Promise の resolve() を呼ぶラッパー関数を作って、それを保持します。

イメージ的には次のようになっています。
コード
  function then( 関数 ) {
    
    promise2 = new Promise((resolve2, reject2) => {
      ラッパー関数 = () => {
        try {
          result = 引数の関数の実行;
          
          if (result is Promise) {
            ((Promise)result).then((_) => {
              resolve2();
            });
          } else {
            resolve2();
          }

          resolve2(result);

        } catch (e) {
          reject2();
        }
      }
      
      ラッパー関数の保持;
    });
    
    return promise2;
  }

resolve() が呼ばれると、then() に渡した関数が実行され、
その後に、then() の内部で生成した Promise の resolve() が実行されます。
これにより、次の then() に渡した関数が実行されます。
コード
      const promise1 = new Promise((resolve, reject) => {
        resolve("OK");
      });
      
      const promise2 = promise1.then((result) => {
        console.log(result);

        // このあとに、内部で生成した promise2 の resolve() が呼ばれる
        // それにより、promise2.then() に指定した関数が実行されるようになる。
      });
      
      const promise3 = promise2.then((result) => {
        console.log(result);
      });

ちなみに、最初に reject() が呼ばれた場合は、then() の内部で生成した Promise の reject() が呼ばれます。


また、then() に指定した関数の中で、次のように Promise を生成して return することがあります。
コード
      .then((result) => {
        const promise = new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve("OK");
          }, 1000);
        });

        return promise;
      })

then() で保持した関数の戻り値が Promise だった場合は、
その Promise の then() を使って、元の Promise の resolve() を呼ぶ関数を保持させます。
コード
          result = 引数の関数の実行;
          
          if (result is Promise) {
            ((Promise)result).then((_) => {
              resolve2();
            });
          }
これにより、then() に指定した関数で resolve() を呼ぶと、その中でさらに resolve() が呼ばれて、次の then() が動きます。

次の記事 >

async、await

YouTube X

新着一覧

  • async、awaitJavaScript
  • Promise についてJavaScript
  • パッケージの管理Node.js
  • v-model(双方向バインディング)Vue.js
  • VS Code で GitHub を使ったソース管理Git
  • computed(再計算)Vue.js
  • watchDebounced(値の監視)Vue.js
  • watch(値の監視)Vue.js
  • change イベントVue.js
  • v-memoVue.js

アーカイブ

  • 2026/03
  • 2026/02
  • 2026/01
  • 2025/12
  • 2025/11
  • 2025/10
  • 2025/09
  • 2025/08
  • /00

以前のカテゴリー一覧

  • CakePHP3
  • CentOS7
  • HTML・CSS・JavaScript
  • Haskell
  • JavaScript
  • Kotlin
  • Laravel5
  • PHP
  • Python
  • Ruby
  • RubyOnRails5
  • TypeScript
  • Vue.js
  • Webサーバ講座
  • Webプログラミング講座
  • jQuery
  • linux
  • パソコン講座
  • ブログ
  • プログラミング講座
  • メモ帳作成講座
  • 数学

Copyright © 9cubed. All Rights Reserved.

プライバシーポリシー 利用規約
▲