Promiseの基本

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

このレッスンで分かること

  • Promise は非同期処理の結果 (まだ未確定の値) を表すオブジェクト
  • 状態は pendingfulfilled / rejected の 3 つ、一度決まると変わらない
  • .then(onFulfilled) / .catch(onRejected) / .finally(onAny) でハンドル
  • async/await は Promise の糖衣構文、現代はそちらが主流

Promiseの基本 とは

Promiseの作成と、then/catch/finallyによるチェーンを練習しよう。本レッスンでは、Promiseの基本 の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。

Promiseの基本

Promiseは非同期処理の結果を表すオブジェクトです。コールバック地獄を解決します。

Promiseの作成

JavaScript

const promise = new Promise((resolve, reject) => { // 非同期処理 if (成功) resolve(結果); else reject(エラー); });

then / catch / finally

JavaScript

promise .then(result => { /* 成功時 */ }) .catch(error => { /* 失敗時 */ }) .finally(() => { /* 常に実行 */ });

チェーン

JavaScript

fetchUser(id) .then(user => fetchPosts(user.id)) .then(posts => console.log(posts)) .catch(err => console.error(err));

この問題では、Promiseチェーンの考え方を同期的に練習します。値に対してステップを順番に適用する関数を実装しましょう。

実務で遭遇するパターン

中級レッスンで学んだことが、実際の業務コードでどう登場するかを整理します。

  • API 呼び出しの基盤fetch の戻り値は Promise
  • 並列処理Promise.all([p1, p2]) で同時待ち、どれかが reject なら全体 reject
  • 順次処理.then(...).then(...) のチェーン
  • タイムアウト実装Promise.race([fetchPromise, timeoutPromise])

コードレビューで指摘されがちなポイント

PR を出すとシニアから入りやすい指摘です。先回りで身につけておけばレビューが一発で通ります。

  • new Promise(executor) を不必要に使うfetch などの Promise を直接返せばよい、ラップ不要
  • .catch 忘れ ─ 未処理 Promise rejection は Node がプロセスを終了することも
  • Promise を return し忘れ.then チェーンで戻り値を return しないと続きが取れない
  • async/await で書けるところで .then チェーン ─ ネストが深いなら書き換え

パフォーマンス考慮事項

  • マイクロタスクキュー.then は次のイベントループより先、setTimeout より早く走る
  • Promise.all で並列化 ─ 複数の非同期 I/O の待ち時間を重ね合わせるため、順次実行より大幅に短縮できる(待ち時間が完全に重なれば最大で並列数ぶん短くなるが、CPU 処理自体が速くなるわけではない)
  • Promise.race のリーク ─ 負けた Promise はキャンセルされない (ネットワークコールは続く)
  • 生成コスト ─ Promise インスタンスは軽量、1 万件 / 秒オーダーなら気にしない
この章のポイント

ここまでの要点 Promise は非同期結果を抱える箱、then / catch / finally でハンドル、async/await でフラットに書ける。並列は Promise.all、競争は Promise.race

よくある質問

Q. Promise と async/await はどっちを使うべき?

A. 新規コードは async/await が圧倒的に読みやすいため第一選択です。Promise.all などで並列実行したいときは Promise メソッドを併用します。最終的に内部では同じ Promise オブジェクトが動いているので、混在しても問題ありません。

Q. rejected の Promise をハンドルしないとどうなりますか?

A. Node.js では UnhandledPromiseRejection の警告が出て、最悪プロセスが落ちます。必ず .catch() を付けるか async 関数内で try/catch で囲んでください。グローバルに process.on('unhandledRejection') で観測する仕組みも入れておくと安心です。

Q. Promise.all と Promise.allSettled の違いは?

A. all は 1 つでも reject すると全体が reject されます。allSettled は全 Promise の結果を {status, value/reason} の配列で返し、失敗があっても他の成功結果は取れます。「全部成功して欲しい」なら all、「個別に結果を集計したい」なら allSettled です。

次のレッスン

次は async/await で、async 関数と await を使って非同期処理を同期的に書く方法を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. Promiseの基本 の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. Promiseの基本 とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

入出力例

test-cases.txt

processSteps(5, [{"action":"double"},{"action":"add","amount":3}]){"error":null,"result":13,"step":null,"success":true} processSteps(3, [{"action":"subtract","amount":5,"validate":true}]){"error":"値が負になりました","result":null,"step":"subtract","success":false} processSteps(10, [{"action":"subtract","amount":3},{"action":"double"},{"action":"add","amount":1}]){"error":null,"result":15,"step":null,"success":true}
main.js
main.js
学習モード

メモ

Promiseの基本

⌘S で保存