JavaScriptの学習を進めていく中で、多くの人が「一番意味がわからない」「コードの動きが追えない」と頭を抱えるのが「非同期処理(ひどうきしょり)」です。
サーバーからデータを引っ張ってきたり、タイマーを使って処理を遅らせたりするときに使う重要な技術ですが、その挙動の特殊さゆえに、ここでプログラミングが嫌いになってしまう初心者もいます。
そんな非同期処理の救世主として登場し、現代のJavaScript開発において必須の共通言語となっているのが「Promise(プロミス)」という仕組みです。
「名前は聞いたことがあるけれど、中身が全然イメージできない」
「thenとかcatchとか、どうしてそんな書き方をするの?」
そんな疑問を抱えている方のために、本記事ではPromiseの概念を「レストランの呼び出しベル」に例えて世界一わかりやすく解説します。
この記事を読めば、Promiseの仕組みが頭の中で綺麗に繋がり、現代主流の文法である「async/await」もスムーズに理解できるようになっていくと思います!
Promiseとは?
JavaScriptを学び進めると、必ずどこかで「非同期処理」という壁にぶつかります。そして、その非同期処理を攻略するための最大の鍵となるのが「Promise(プロミス)」です。
まずは結論からお伝えします。Promiseを一言で表現すると、「非同期処理の結果(未来に返ってくる値)を管理する仕組み」です。
javaScriptは、サーバーからデータを取得したり、タイマーを使った処理をしたりするとき、その処理が終わるのを待たずに次のコードを実行する性質(非同期処理)を持っています。
Promiseは、その「あとから返ってくる結果」が成功したのか、それとも失敗したのかを、人間にとってもプログラムにとっても扱いやすく整理してくれる画期的な仕組みなのです。
現代のJavaScript開発において、APIとの通信やフレームワーク(ReactやVue、Next.jsなど)を扱う上で、Promiseの理解は「絶対に避けて通れない必須知識」となっています。
レストランに例えるとイメージしやすい!
目に見えないプログラムの動きを、レストランの注文に例えて見ましょう。
【レストランでの流れ】
1. 注文する ────> 店員から「呼び出しベル(Promise)」を渡される
2. 料理を待つ ──> ベルを持ったまま、席でスマホを見て待つ(他の作業ができる=非同期)
3. 完成! ────> ベルが鳴り、カウンターで料理(成功データ)を受け取る
Promiseとは、まさにこの「呼び出しベル」です。 注文(処理の開始)した瞬間、料理(結果)はまだ手元にありませんが、「あとで結果を返します」という約束(Promise)の証としてベルを受け取ります。これがあるおかげで、私たちは料理が届くまでの間、ずっとカウンターの前に縛り付けられることなく、席で別の作業(他のコードの実行)をして待つことができるのです。
なぜPromiseが必要なのか?
「仕組みがわかったけれど、なぜわざわざPromiseなんてものを使うの?」と感じるかもしれません。その理由は、Promiseが登場する前のJavaScriptが抱えていた、ある「深刻な問題」にあります。
コールバック関数だけでは管理が大変
Promiseが登場する前、非同期処理の結果を受け取るには「コールバック関数(処理が終わったらこの関数を実行してね、とあらかじめ渡しておく関数)」を使うしかありませんでした。
簡単な処理であれば問題ないですが、少し複雑なことをやろうとすると、コードが途端に牙を剥きます。
処理が増えると読みにくくなる(コールバック地獄)
例えば、「①ユーザー情報を取得し、②その情報を使って投稿一覧を取得し、③その中から最新のコメントを取得する」という、実務でよくある連続した非同期処理をコールバック関数だけで書くと、以下のようになります。
// コールバック関数による泥沼のコード(コールバック地獄)
getUser((user) => {
getPosts(user.id, (posts) => {
getComments(posts[0].id, (comments) => {
console.log(comments);
// さらに処理が続くと右側にどんどんネストが深くなる…
});
});
});
コードが右側へ「>」の形にどんどん深く重なっていきます。これが、開発者の間で恐れられている「コールバック地獄(Pyramid of Doom)」です。どこでエラーが起きたのか判別しづらく、修正するのも一苦労という最悪の可読性でした。
Promise
Promiseを使うと、この地獄のような入れ子構造を、上から下へ流れるような一本道の美しいコードに書き換えることができます。
// Promiseを使ったスッキリしたコード
getUser()
.then(user => getPosts(user.id))
.then(posts => getComments(posts[0].id))
.then(comments => console.log(comments));
縦に綺麗に並びましたね。これが現代のJavaScriptでPromiseが必須とされる最大の理由です。
Promiseの3つの状態
Promiseを正しく使いこなすために、必ず覚えておくべき「3つの状態」があります。呼び出しベルが今どういう状況なのかを表すステータスです。
Promiseは必ずpendingからスタートし、最終的にfulfilled(成功)かrejected(失敗)のどちらか一方の形にしか変化しません。一度結果が決まれば、状態が覆ることはありません。
Promiseの基本的な書き方
では、実際にコードを書いてみましょう。まずは「Promiseを作る側」と「結果を受け取る側」の基本構文です。
基本構文
Promiseはnew Promise( )を使ってインスタンスを作ります。引数には、成功を通知するためのresolveと、失敗を通知するためのrejectという2つの関数を受け取ります。
const myPromise = new Promise((resolve, reject) => {
// ここに非同期処理(タイマーや通信など)を書く
if (/* 処理が成功したら */) {
resolve("成功データ"); // 状態を fulfilled に変える
} else {
reject("エラーメッセージ"); // 状態を rejected に変える
}
});
簡単なサンプル
1秒後にデータを取りに行く処理をシミュレーションしてみましょう。
// 1. Promiseを準備する(作る側)
const fetchUserData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = true; // わざと成功させる
if (success) {
resolve({ id: 1, name: "Narudev" }); // 成功データを渡す
} else {
reject("データの取得に失敗しました");
}
}, 1000); // 1秒待つ
});
};
// 2. Promiseを実行して結果を受け取る(使う側)
fetchUserData()
.then((user) => {
console.log("成功!", user.name); // 1秒後に「成功! Narudev」と表示される
})
.catch((error) => {
console.error("エラー!", error);
});
thenとは?
Promiseの後ろにくっついている.then( )の役割を深掘りします。
成功したときに実行される
.then( )は、Promiseの状態がpendingからfulfilled(成功)に変わった瞬間に呼び出される仕掛けです。
データを受け取れる
resolve(値)で渡された中身が、そのまま.then(値 => { . . . })の引数として入ってきます。上のサンプルでいうと、resolveに入れたオブジェクトを、.thenの中のuserとして受け取って画面に出力しています。
catchとは?
実務のプログラミングにおいて、成功時と同じくらい重要なのが.catch( )です。
失敗したときに実行される
何らかのトラブル(通信エラーやバグ)が発生し、Promise内でreject(エラー原因)が実行されると、状態はrejected(失敗)になります。その瞬間に、.then( )をすべてスキップして、この.catch( )の中に処理が飛び込んできます。
エラー処理ができる
実務では、インターネットの接続切れなどでデータが取れないことが日常茶飯事です。.catch( )の中で「通信に失敗しました。時間をおいて再度お試しください」と画面にエラー表示を出すなど、アプリを安全に動かし続けるための重要な役割を担っています。
Promiseを使った処理の流れ
全体の流れを頭の中で整理してみましょう。
- fetchUserData( )が呼び出され、pending(処理中)のPromise(呼びだしベル)が返される。
- バックグラウンドで1秒のタイマー(非同期処理)がスタート。この間、JavaScriptは他のコードの実行を止めない。
- 1秒が経過し、データが正常に準備ができたのでresolve( )が呼ばれる。
- Promiseの状態がfulfilled(成功)へと変化する。
- 変化を検知して.then( )が起動し、受け取ったデータを処理する。
Promiseでよくある間違い
実際の現場で見かける「Promiseの誤解」を3つ共有します。
結果をすぐ取得できると思う(非同期の罠)
// ❌ 間違いの例
const result = fetchUserData();
console.log(result); // > Promise { <pending> } と表示されて中身が見えない!
Promiseはあくまで「呼び出しベル」です。中身の料理はあとから届くため、このように普通に変数をコンソールに出力しても、まだ中身は空っぽ(pending)です。データを使いたい場合は、必ず.then( )の中で処理をしなければなりません。
thenの中でしか使えない値がある
.then((data) => { . . . })の中で受け取ったdataは、そのスコープ(波括弧 { } の中)でしか生きられません。外側のグローバル変数に無理やり代入しようとしても、タイミングのずれでうまく動かない原因になります。
catchを書かない(実務では危険)
「絶対に成功するはずだから」と.catch( )を省略する人がいますが、これは現場ではご法度です。万が一エラーが発生した際、エラーがどこにも捕まらずにアプリ全体がクラッシュして画面が真っ白になる原因(Uncaught in Promiseエラー)になります。Promiseを使うときは、義務教育だと思って必ず.catch( )をセットで書きましょう。
現在はasync/awaitが主流
ここまでPromiseの基礎と美しさを解説してきましたが、現代(2026年現在)の実務でPromiseをそのまま.then( ) .catch( )で書く機会は、実は少し減っています。
なぜなら、Promiseをさらに進化させた「async/await」という、より直感的に書ける文法が主流だからです。
サンプルコードの比較(中身はどちらも同じPromise)
どれくらい見やすさが変わるか、2つの書き方を比べてみましょう。
// 【パターン1】従来のPromise(thenチェーン)
const displayUser = () => {
fetchUserData()
.then(user => {
console.log(user.name);
})
.catch(err => {
console.error(err);
});
};
// 【パターン2】現在の主流 async/await
const displayUser = async () => {
try {
const user = await fetchUserData(); // まるで同期処理(上から順に進むコード)のように書ける!
console.log(user.name);
} catch (err) {
console.error(err);
}
};
async/awaitを使うと、.then( )の入れ子すら消え去り、「非同期処理なのに、普通の処理と同じように上から下へ素直に読めるコード」になります。
「じゃあPromiseの勉強は無駄だったの?」と思うかもしれませんが、全くそんなことはありません。awaitは、「後ろに続くPromiseの状態がfulfilledになるまでその場で待つ」という命令です。つまり、Promiseの仕組み(3つの状態や中身の概念)を100%理解していなければ、async/awaitを正しく使うこなすことは不可能なのです。
まとめ
JavaScriptの非同期処理の救世主「Promise」のまとめです。
- Promiseとは:非同期処理の「未来の結果」を管理し、コールバック地獄を防ぐ仕組み。
- 3つの状態:pending(処理中)> fulfilled(成功)またはrejected(失敗)へ変化する。
- 成功時:.then( )でresolveされたデータを受け取る。
- 失敗時:.catch( )で安全にエラー処理を行う(省略はNG)。
- ネクストステップ:Promiseの基礎があるからこそ、現代主流のasync/awaitの理科へと繋がる。
非同期処理は、最初は頭がこんがらがるかもしれませんが、レストランの呼び出しベルを思い出しながら何度もコードを書いてみてください。仕組みがカチッと繋がる瞬間が必ず訪れます。

