PR

【JavaScript】Promiseとは?then・catch・finallyとasync/awaitの読み方

Promiseが非同期処理の結果をthenとcatchへ渡す流れを表す統一フォーマットのアイキャッチ JavaScript

JavaScriptのPromiseとは、すぐには終わらない処理の結果を、あとで受け取るための仕組みです。

Promiseが分かると、fetchthencatchasync/await のコードを「何を待って、どこで結果を受け取るのか」という流れで読めるようになります。

Promiseは「結果そのもの」ではなく、「あとで結果を受け取る入口」です。最初はこの理解だけで十分です。

この記事では、Promiseの意味、3つの状態、thencatchfinallyの使い方、fetchでつまずきやすいポイント、async/awaitとの関係を順番に解説します。

スポンサーリンク

まず結論:Promiseは将来の結果を表すオブジェクト

Promiseは、非同期処理の最終的な成功または失敗を表すオブジェクトです。MDNでも、Promiseは非同期処理の完了または失敗と、その結果の値を表すものとして説明されています。

ただし、初心者のうちは仕様文を暗記するより、次の読み方を先に覚える方が実用的です。

非同期処理を始める
  ↓
Promiseが返る
  ↓
成功したら then
失敗したら catch
最後に finally

この時点で大事なのは、Promiseが返ってきても、まだ中身の結果を直接持っているとは限らないことです。結果はあとから成功または失敗として確定します。

Promiseが必要になる場面

JavaScriptでは、通信、タイマー、ファイル読み込みのように、すぐ終わらない処理がよく出てきます。特にWebアプリでは、APIからデータを取るfetchが代表例です。

通信が終わるまで画面全体を止めると、ユーザーは操作できません。そのためJavaScriptでは、通信を待っている間も他の処理を進め、結果が返ってきたときに続きを実行します。

この「あとで続きを実行する」ための約束を整理するのがPromiseです。

Promiseの流れを図で見る

次の図では、Promiseを「非同期処理の結果を、成功・失敗・最後の処理へ分けて受け取る流れ」として見てください。細かい用語より先に、左から右への読み順を作るのがポイントです。

JavaScriptのPromiseが非同期処理の結果をthen、catch、finallyへ渡す流れを示す図
Promiseは値そのものではなく、非同期処理の結果を後から受け取るための入口として見ると理解しやすくなります。

pending・fulfilled・rejectedの3状態

Promiseには状態があります。まずは次の3つだけ押さえれば十分です。

状態意味初心者向けの見方
pendingまだ結果が出ていない処理中
fulfilled成功して結果が得られた成功
rejected失敗して理由が返った失敗

Promiseは最初にpendingになり、その後、成功すればfulfilled、失敗すればrejectedになります。成功と失敗のどちらかに決まった状態を、実務では「settled」と表現することもあります。

then・catch・finallyの役割

Promiseでよく使うのは、thencatchfinallyです。

メソッド役割よくある使い方
then()成功時の結果を受け取る取得したデータを画面に出す
catch()失敗時の理由を受け取るエラー表示やログ出力をする
finally()成功・失敗に関係なく最後に動くローディング表示を消す
fetch("/api/users")
  .then((response) => response.json())
  .then((users) => {
    console.log(users);
  })
  .catch((error) => {
    console.error("取得に失敗しました", error);
  })
  .finally(() => {
    console.log("読み込み処理を終了します");
  });

ポイントは、前のthenで返した値が、次のthenへ渡されることです。response.json()を返すから、次のthenで変換後のデータを受け取れます。

fetchでよくある誤解:HTTPエラーは必ずcatchに入るわけではない

Promiseをfetchで使うとき、初心者がつまずきやすいのがエラー処理です。ネットワーク自体に失敗した場合はcatchに入りますが、HTTPステータスが404や500の場合は、必ずしも自動でcatchに入るとは限りません。

fetchの結果では、response.okを見て、HTTPとして成功扱いできるかを確認するのが安全です。

fetch("/api/users")
  .then((response) => {
    if (!response.ok) {
      throw new Error("HTTPエラー: " + response.status);
    }
    return response.json();
  })
  .then((users) => {
    console.log(users);
  })
  .catch((error) => {
    console.error(error);
  });

このように書くと、通信そのものの失敗だけでなく、サーバーがエラーを返した場合も同じcatchで扱いやすくなります。

async/awaitはPromiseを読みやすくする書き方

async/awaitは、Promiseと別物ではありません。Promiseを、上から順に読む形で書きやすくするための構文です。

async function loadUsers() {
  try {
    const response = await fetch("/api/users");

    if (!response.ok) {
      throw new Error("HTTPエラー: " + response.status);
    }

    const users = await response.json();
    console.log(users);
  } catch (error) {
    console.error(error);
  }
}

awaitはPromiseの完了を待ち、その結果を取り出します。つまり、async/awaitを使っていても、裏側ではPromiseを扱っています。

最初に覚える判断基準

  • fetch()の戻り値はPromiseとして読む
  • thenでは次へ渡したい値をreturnする
  • HTTPステータスの失敗はresponse.okで確認する
  • async/awaitはPromiseを消すものではなく、読みやすくする書き方と考える

Promise.allは最初から暗記しなくてよい

Promiseを調べると、Promise.allPromise.racePromise.allSettledなども出てきます。これらは複数のPromiseをまとめて扱うための機能です。

ただし、初学者が最初に全部を覚える必要はありません。まずは、1つのPromiseが成功・失敗へ分かれる流れを読めることが先です。複数の通信を同時に待つ必要が出てきたときに、Promise.allを学ぶ順番で問題ありません。

たとえば、ユーザー情報と注文履歴を同時に取得したい場合は、複数のPromiseをまとめて待つ考え方が必要になります。一方、単に1つのAPIからデータを取るだけなら、thencatchfinallyとasync/awaitを理解していれば十分です。

コードレビューで見るポイント

Promiseを使ったコードを読むときは、まず「どこでPromiseが作られ、どこで結果を取り出しているか」を見ます。次に、失敗時の処理があるか、ローディング表示などの後片付けが漏れていないかを確認します。

特にWeb画面では、通信中の表示、成功時の表示、失敗時の表示がそろっているかが重要です。Promiseの文法だけでなく、ユーザーに何が見えるかまで考えると、実務のコードを読みやすくなります。

公式情報と関連して読みたい記事

まとめ

Promiseは、非同期処理の結果をあとで受け取るための仕組みです。最初は「将来の結果を表すオブジェクト」と考えれば十分です。

  • 成功時はthen
  • 失敗時はcatch
  • 最後の処理はfinally
  • fetchではresponse.okも確認する
  • async/awaitはPromiseを読みやすくする構文
タイトルとURLをコピーしました