次の記事 >
async、await
[JavaScript]Promise について
公開日:2026-03-19
更新日: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>
実行結果
コード
OK2.2 説明
Promise の生成時に、非同期処理を行う関数を渡します。
非同期処理が正常終了した場合、resolve() を呼ぶと、then() に指定した関数が実行されます。
エラーが発生した場合、reject() を呼ぶと、catch() に指定した関数が実行されます。
非同期処理が正常終了した場合、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:OK3.2 説明
new Promise() に渡した関数は、同期的にすぐに実行されます。
then() と catch() は、引数の関数の保持だけ行い、まだ関数は実行しません。
そのため、最後の console.log("2") が2番目に出力されています。
その後、1秒後に setTimeout() に渡した関数が実行されます。
この関数の中で resolve() を呼んでいますが、これにより、then() に渡した関数が実行されます。
但し、resolve() の中で直接 then() の関数を実行しているのではなく、現在の関数を抜けてから別のタイミングで実行されます。
そのため、console.log("4") が then() よりも先に実行されています。
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
84.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() を実行した方の状態になります。
.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 になった時の処理を書くことができます。
上記のコードの最後の catch() の関数は、then() の 2番目の引数の関数で正常終了しているため、実行されません。
then() に渡した関数の中で throw new Error("error"); すると、実行されます。
また、then() は2つの関数を受け取れますが、catch() は rejected 用の関数1つだけです。
これにより、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秒経過
Error6.2 説明
then() の関数の中で、さらに非同期処理を実行することができます。
この場合、Promise を生成して、その Promise を return します。
そうすると、元の Promise の状態は、return された Promise の状態を引き継ぐようなイメージになります。
そして、生成した Promise の reject() を呼ぶと、状態が rejected となり、後の catch() の関数が実行されるようになります。
この場合、Promise を生成して、その Promise を return します。
そうすると、元の Promise の状態は、return された Promise の状態を引き継ぐようなイメージになります。
そして、生成した Promise の reject() を呼ぶと、状態が rejected となり、後の catch() の関数が実行されるようになります。
7. イベントループ、マクロタスク、マイクロタスク
JavaScript は、イベントループと言うループがあり、
そこで、マクロタスクキューとマイクロタスクキューのタスクを実行しています。
イベントループのイメージ
マクロタスクは、1回のイベントループで1つだけ実行します。
マイクロタスクキューは、キューが空になるまで実行します。
そのため、マイクロタスクキューにタスクがある限り、後ろのレンダリング処理が実行されないため、画面がフリーズします。
マクロタスク
・setTimeout
・setInterval
・DOMイベント(click、keydown)
・I/Oコールバック(ファイル入出力など)
など
マイクロタスク
・Promise.then()、Promise..catch()、 Promise.finally()
・queueMicrotask
など
余談:setInterval() は一定間隔でマクロタスクキューにタスクを追加する際に、前回追加した処理が終了したかどうかは見ていません。
そのため、重い処理をする場合は、マクロタスクキューがずっと溜まっていく可能性があります。
その場合は、setTimeout() を使い、タスクの最後に setTimeout() を呼ぶことで、安全に一定間隔で処理させることができます。
そこで、マクロタスクキューとマイクロタスクキューのタスクを実行しています。
イベントループのイメージ
コード
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 タグを分けると、動作が変わります。
そのため、2つ目のマクロタスクが実行される前に、マイクロタスクが実行されます。
そのため、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)を取得して、メソッドチェーンを使わずに書くこともできます。
then() と catch() は新しい Promise を生成しているため、上記の promise1、promise2、promise3 は、全て異なるオブジェクトとなります。
これは 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() を呼ぶラッパー関数を作って、それを保持します。
イメージ的には次のようになっています。
resolve() が呼ばれると、then() に渡した関数が実行され、
その後に、then() の内部で生成した Promise の resolve() が実行されます。
これにより、次の then() に渡した関数が実行されます。
ちなみに、最初に reject() が呼ばれた場合は、then() の内部で生成した Promise の reject() が呼ばれます。
また、then() に指定した関数の中で、次のように Promise を生成して return することがあります。
then() で保持した関数の戻り値が Promise だった場合は、
その Promise の then() を使って、元の Promise の resolve() を呼ぶ関数を保持させます。
この時に、引数の関数をそのまま保持するのではなく、
関数実行後に、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

