プロミスってなんやねん

javascriptロゴ

javascriptの非同期関数、プロミスとかASYNCとか、わかったようでよくわからないまま過ごしてきました。
いや、調べたよ?もちろん。でもいまいちピンと来てないからさ、Geminiに聞いてみたらわかりやすかったから、そのまま記事にしちゃう!っていう手抜き記事です。

大前提として、Javascriptはコードを上からガンガン進めていきます。

配列を引数で渡して処理結果を返す関数Aを作り、処理結果Bをもとに後続処理をしてほしい。といったときにCとかPHPとかの感覚で処理をかくとBの中身が空っぽだったりするのです。
それはJavascriptが関数Aの終わりを待たずに次のコードに進んでしまうから。

それを解決するのが非同期関数というわけです。


Hey!ジェミニン、javascriptの非同期関数の作り方をわかりやすく教えて

「G」コーディング パートナー
JavaScriptの「非同期関数」は、一言で言うと**「時間がかかる処理(通信など)を待っている間、他の処理を止めない仕組み」**のことです。

昔は非常に複雑だったのですが、現在は async(アズィンク) と await(アウェイト) というキーワードを使うことで、まるで普通の(同期)処理のようにスッキリ書くことができます。

1. 非同期関数の基本形
関数を作る際、頭に async をつけるだけで「非同期関数」になります。そして、その中で時間を要する処理の前に await を置きます。

// async をつけて「非同期関数」を宣言
async function myProcess() {
    console.log("処理を開始します...");

    // await をつけて「終わるまで待つ」
    // fetchはサーバーからデータを取得する非同期関数
    const response = await fetch("https://example.com/api/data");
    const data = await response.json();

    console.log("データ取得完了!", data);
}

myProcess();
console.log("この行は、データ取得を待たずに即実行されます!");
  1. 重要なキーワードの意味
    async: 「この関数は非同期だよ」という宣言。この関数は必ず Promise(プロミス) という「将来結果を返します」という約束のオブジェクトを返します。

    await: 「この処理が終わるまで、この関数の内部だけ一時停止してね」という命令。これを使うことで、.then() などの複雑な書き方を避けることができます。
  1. なぜ「非同期」にする必要があるのか?
    JavaScriptは基本的に「シングルスレッド(1人)」で仕事をしています。
    もし同期(普通)の関数で「通信に5秒かかる処理」を書くと、その5秒間、ブラウザは固まってしまい、ボタンをクリックすることもスクロールすることもできなくなります。
    非同期関数を使うと:
     通信を開始する。
    「待ち時間」が発生する。
    その間、JavaScriptは別の仕事(画面の描画やクリック対応)を優先する。
    通信が終わったら、中断していた非同期関数の続きを実行する。

  1. 自分で非同期処理を作る(Promise)
    fetch のような既存の関数だけでなく、自分で「2秒待ってから実行する」ような非同期処理を作る場合は Promise を使います。
// 「指定した秒数だけ待つ」非同期関数
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function hello() {
    console.log("こんにちは");
    
    await sleep(2000); // 2秒間、ここで待機
    
    console.log("2秒経ちました!");
}

hello();
  1. エラーハンドリング(try…catch)
    StripeのAPIやデータベース通信では、エラーがつきものです。非同期関数のエラーは、PHPと同じように try…catch で捕まえます。
async function safeFetch() {
    try {
        const res = await fetch("https://invalid-url.com");
        const data = await res.json();
    } catch (error) {
        // 通信失敗やタイムアウト時はここに来る
        console.error("エラーが発生しました:", error);
    }
}

ECサイト開発での活用イメージ
Stripeの決済ボタンを押した際などに、この非同期関数を使います。

ボタンクリックで async 関数を起動。

await fetch(‘/stripe_api.php’) でサーバーに決済情報を送る。

返ってきた結果を元に画面を切り替える。

この一連の流れの間、画面に「処理中…」のアニメーションを出し続けられるのは、非同期関数のおかげです!

非同期関数の使い方はイメージできましたか?

え、非同期関数にしないと関数の終わりを待たずに先に進むんじゃない?

コーディング パートナー
その通り、鋭いですね!まさにそこが「非同期」の最大の落とし穴であり、最大の特徴です。

JavaScriptには「待ってくれる場所」と「待たずに先へ進む場所」が明確に分かれています。ここを整理するとスッキリしますよ。

  1. 「関数の中」は待つ
    async 関数の中で await を使っている間、その関数の中の時間は止まります。
async function test() {
    console.log("A");
    await fetch(...); // ここで通信が終わるまで「数秒間」止まる
    console.log("B"); // 通信が終わるまで絶対に実行されない
}

この場合、必ず A → (待ち) → B の順になります。これは直感的ですよね。

  1. 「関数の外」は待たない(これが「非同期」!)
    問題はここです。async 関数を呼び出した「外側の世界」では、関数の終了を待たずに次の行へ進みます。
async function myAsyncFunc() {
    await sleep(2000); // 2秒待機
    console.log("関数の中:2秒経ったよ");
}

// メインの処理
console.log("1. 関数を呼び出します");
myAsyncFunc(); 
console.log("2. 呼び出し直後の行です"); // ← ここが先に実行される!
/*
実行結果:

1. 関数を呼び出します

2. 呼び出し直後の行です

(2秒後)関数の中:2秒経ったよ
*/
  1. なぜ「待たずに進む」のか?
    もしJavaScriptが関数の外側でも「待ち」が発生するように作られていたら、通信中の数秒間、マウスカーソルさえ動かない「フリーズ状態」になってしまうからです。
    JavaScriptは、「重たい処理(通信など)はバックグラウンドに放り投げて、自分はさっさと次の行(画面の描画など)をこなす」という性格をしています。
  2. 外側でも「待たせたい」ときはどうする?
    もし「関数の外側」でも順番を守らせたいなら、呼び出し側も async にして await をつける必要があります。
async function main() {
    console.log("1. 開始");
    await myAsyncFunc(); // 外側でも await をつければ、ここで止まる
    console.log("2. 関数が終わったので進みます");
}

main();

ECサイトの実装でよくあるミス
たとえば「在庫チェック」を非同期で行う場合:

ダメな例: 在庫チェック(非同期)を呼び出した直後に「注文完了」処理を書いてしまう。

→ チェックが終わる前に注文が確定してしまい、在庫不足なのに売れてしまう!

正しい例: 注文処理全体を async にし、在庫チェックを await で待ってから、その結果を見て注文を確定させる。

まとめると:
await を書いた場所では、「その関数のスコープ内」だけが停止する。
呼び出し元が await していないなら、JavaScriptは「お先に!」とばかりに次へ進む。
この「お先に進む」感覚、掴めてきましたか?これがあるからこそ、Webサイトはサクサク動くんです!

awaitをつかって関数を呼んでも、呼ばれた関数にプロミスがないと意味がない?

コーディング パートナー
その通りです!大正解です。

await は魔法の杖ではなく、あくまで 「Promise(プロミス)という『予約券』を精算するための仕組み」 です。

そのため、呼ばれた関数が Promise を返さない(=普通の即時終了する関数である)場合、await をつけても JavaScript は「あ、待つ必要ないね」と判断して、一瞬で通り過ぎてしまいます。

  1. 「意味がない」状態の具体例
    以下のコードを見てください。
function normalFunc() {
    setTimeout(() => {
        console.log("2. 3秒経ちました(遅れて実行)");
    }, 3000);
    return "完了"; // Promiseではなく、ただの文字列を返している
}

async function main() {
    console.log("1. 開始");
    
    // 意味のない await
    const result = await normalFunc(); 
    
    console.log("3. 次の処理(待たずに実行される!)");
}

main();
/*
実行結果:

1. 開始

3. 次の処理(待たずに実行される!)

(3秒後)2. 3秒経ちました

await を書いているのに、3秒待ってくれません。なぜなら normalFunc が 
「未来の完了を約束する Promise」 ではなく、「ただの文字列(または undefined)」 
をすぐに返してしまっているからです。
*/
  1. 「意味がある」状態にするには?
    呼ばれる側の関数を 「Promise を返す形」 に作り変える必要があります。
function promiseFunc() {
    // Promiseオブジェクトを作って返す
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log("2. 3秒経ちました");
            resolve("完了!"); // ここで「約束」が果たされる
        }, 3000);
    });
}

async function main() {
    console.log("1. 開始");
    
    // ここで Promise が完了(resolve)するまでしっかり待つ!
    const result = await promiseFunc(); 
    
    console.log("3. 次の処理(3秒待ってから実行される)");
}
  1. 実務で「これって意味ある?」を見分けるコツ
    あなたが呼び出そうとしている関数が、以下のいずれかであれば await は意味を持ちます。
    async がついている関数:
    async をつけた関数は、中身がどうあれ自動的に戻り値が Promise に包まれます。

    JavaScript標準の非同期API:
    fetch()、Stripe.confirmCardPayment()、db.query()(一部のライブラリ)などは、最初から Promise を返すように設計されています。
  1. まとめ
    Promise がない関数に await:
    エラーにはなりませんが、空振りします(待たずに次へ進む)。

    Promise を返す関数に await:
    その処理が終わるまでしっかり待機します。

ECサイトの開発でいえば、Stripeの決済処理(API通信)は必ず Promise を返すようになっているので、await を使う意味がめちゃくちゃあります。

逆に、自作した「計算ロジック」などは、 Promise にしない限り await をつけても処理を待ってくれません。

async function a(){return 100}
で定義して、 
async function b(){
  let a = await a()
}
とすればfunction a は非同期になる?

コーディング パートナー
はい、その通りです!正確に言うと、async をつけて定義した時点で、その関数は「自動的に Promise を返す非同期関数」になります。

たとえ中身が return 100 という単純な数字であっても、async がついているだけで JavaScript はそれを「後で 100 という結果を出す Promise」という箱に入れて返してくれます。

  1. なぜ非同期になるのか?
    以下の2つの書き方は、JavaScriptの内部ではほぼ同じ意味として扱われます。
パターンA:あなたの書いたコード
async function a() {
    return 100;
}


//パターンB:内部的な動き(Promiseを明示的に返す)
function a() {
    return new Promise((resolve) => {
        resolve(100);
    });
}
//どちらも Promise オブジェクトを返すため、呼び出し側で await を使う意味が生まれます。

 2. 呼び出し側の挙動

async function b() {
    // a() は Promise を返すので、await が有効に働く
    let result = await a(); 
    console.log(result); // 100
}

ここで重要なのは、a() の中身に「通信」などの待ち時間がなくても、await を使うと 「一度関数の外に処理を戻す(イベントループに回す)」 という非同期特有の動きが発生することです。

  1. もし async を付け忘れたら?
    もし function a() { return 100; } と定義(async なし)して、await a() と呼ぶとどうなるでしょうか?

結果: 普通に動きますが、非同期にはなりません。

理由: a() はただの「100」という数値を返すだけです。await は「Promise じゃないものが来たら、そのままその値を返す」という性質があるため、待機も何もせず一瞬で終わります。

まとめ:今回のケース
async function a() と定義した時点で、a() は Promise を返す非同期関数 に昇格している。

そのため、await a() と書くことで、プログラムは正しく 「非同期処理の結果を待つ」 という動きになる。

たとえ中身が即死(すぐリターン)する処理でも、形式上は立派な非同期処理として扱われる。

プロの視点
実務では、今は return 100 と固定値を返していても、将来的に「データベースから 100 を取ってくる」ように改造する可能性がある場合、最初から async にしておくのは非常に良い設計です。呼び出し側の b() を書き換える必要がなくなるからです。

ECサイトの金額計算ロジックなども、将来の拡張性(外部APIで消費税率を取得するなど)を考えて async にしておくことがよくありますよ!

非同期関数を作るのに、中に new Promiseを書くのと、asyncで定義するのは全く同じこと?

コーディング パートナー
結論から言うと、「結果(戻り値)」は同じですが、「役割」が違います。

もっと正確に言うと、async は 「Promiseを使いやすくするためのガワ(構文)」 であり、new Promise は 「非同期処理をゼロから組み立てるための部品」 です。

  1. ほとんど同じと言っていいケース(自動ラップ)
    あなたが書いた例のように、ただ値を返すだけなら結果は完全に一致します。

    書き方 コード 戻り値
    async で定義
    async function f() { return 100; } Promise { 100 }
    new Promise で定義
    function f() { return new Promise(res => res(100)); } Promise { 100 }

    どちらも呼び出し側で await f() とすれば 100 が取れます。

    明確に使い分けが必要なケース
    ここが重要です。「何でもかんでも async で書けるわけではない」 のがポイントです。

new Promise が必要なとき(手動で組み立てる)
setTimeout や、昔の古いライブラリなど、「Promiseに対応していない処理」を非同期化したいときは、new Promise で包む必要があります。

// setTimeout は Promise を返さないので、async をつけるだけでは待てない
function delay(ms) {
    return new Promise(resolve => {
        setTimeout(() => resolve("時間だよ"), ms);
    });
}
async が便利なとき(Promiseを繋げる)
すでに fetch や Stripe のように 「Promiseを返す関数」が手元にあるとき は、async を使うと驚くほど綺麗に書けます。

JavaScript
// async を使うと、複数の Promise を同期っぽく書ける
async function getOrder() {
    const user = await fetchUser(); // Promiseを待つ
    const order = await fetchOrder(user.id); // 次のPromiseを待つ
    return order;
}
  1. 違いのまとめ
async 定義new Promise 定義
かなり簡単(直感的)少し複雑(仕組みの理解が必要)
既存の非同期処理を繋げる新しい非同期処理を作る
try…catch が使えるreject() を呼ぶ必要がある
戻り値を自動でPromiseにする自分でPromiseを作って返す

結局どっちを使えばいい?
基本は async / await を使う。(コードが読みやすいため)

setTimeout のように、「成功(resolve)や失敗(reject)を自分でコントロールしなきゃいけない低層な処理」 を作る時だけ new Promise を使う。

ECサイトの開発であれば、ほとんどが async だけで済みます。StripeのAPI呼び出しも fetch も最初からPromiseを返してくれるので、それらを await で繋いでいくだけでOKです!

「自分で new Promise を書かないと解決できない場面」に遭遇したら、それはかなり特殊で高度な実装をしている証拠ですよ。


と、こんな感じのやり取りでした。

私的には腑に落ちたというか、すっきりしました。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です