{}const=>[]async()letfn</>var
開発JS

JavaScriptのプロミス:非同期の完全ガイド

JavaScript での Promise を理解する: 3 つの Promise 状態、then/catch/finally チェーン、Promise.all メソッド、Promise.race メソッド。開発者向けの非同期コードの実用例、一般的なエラー、ベストプラクティス。

К

Kodik

著者

3分で読める

約束とは

Promiseは、非同期操作の結果を表すオブジェクトであり、現在、将来、または決して取得できないものです。これは、操作が完了したときに結果を返すという「約束」のようなものです。

Promisは次の3つの状態のいずれかで存在します。

  • pending (待機)—初期状態、操作はまだ実行中

  • fulfilled (完了)—操作は正常に完了しました

  • rejected (却下)—操作はエラーで終了しました

promiseは、保留中の状態から一度だけ、実行済みまたは拒否済みのいずれかに移行できることを理解することが重要です。その後、状態は変わりません。

🔥 10万人以上の学生が参加中

理論を読むのに疲れた?
コーディングの時間だ!

Kodik — 実践でプログラミングを学ぶアプリ。AIメンター、インタラクティブなレッスン、実際のプロジェクト。

🤖 AI 24時間
🎓 修了証
💰 無料
🚀 始める
今日参加

プロミスの作成

約束は、実行者関数を受け取るコンストラクター Promise を使用して作成されます。この関数は、resolverejectという2つの引数を受け取ります。

const myPromise = new Promise((resolve, reject) => {
  // 非同期操作
  const success = true;
  
  if (success) {
    resolve('The operation was successful!');
  } else {
    reject('An error occurred');
  }
});

実行関数は、約束が作成されたときにすぐに開始されます。resolve(value)を呼び出すと、promiseはvalueの結果でfulfilled状態になります。reject(error)を呼び出すと、promiseはエラーerrorでrejectedに変わります。

結果の処理: then, catch, finally

promiseの結果を処理するには、thencatch、およびfinallyメソッドを使用します。

myPromise
  .then(result => {
    console.log('Success:', result);
    return result.toUpperCase();
  })
  .catch(error => {
    console.error('Error:', error);
  })
  .finally(() => {
    console.log('Operation completed');
  });

メソッド then は、2つのオプションの引数を受け取ります。正常に実行された場合のコールバックと、エラーの場合のコールバックです。実際には、エラー処理にはcatchがより頻繁に使用され、コードがより読みやすくなります。

メソッド finally は、約束の結果に関係なく常に実行されます。これは、読み込みインジケーターを非表示にするなど、いずれにせよ実行する必要があるアクションに便利です。

プロミスチェーン

約束の主な利点の1つは、非同期操作のチェーンを作成できることです。各thenは、シーケンスを構築できる新しいpromiseを返します。

fetch('https://api.example.com/user/1')
  .then(response => response.json())
  .then(user => {
    console.log('Username:', user.name);
    return fetch(`https://api.example.com/user/${user.id}/posts`);
  })
  .then(response => response.json())
  .then(posts => {
    console.log('User posts:', posts);
  })
  .catch(error => {
    console.error('Error in the chain:', error);
  });

プロミスのチェーンでは、エラーはどの段階でも最も近い catchに「落ち込みます」。これにより、エラーを一元的に処理できます。

実例:データの読み込み

実際のシナリオを考えてみましょう。さまざまな状況を処理して、サーバーからユーザーデータを読み込みます。

function fetchUserData(userId) {
  return new Promise((resolve, reject) => {
    // ネットワーク遅延のシミュレーション
    setTimeout(() => {
      const users = {
        1: { name: 'Alexey', role: 'developer' },
        2: { name: 'Maria', role: 'designer' }
      };
      
      const user = users[userId];
      
      if (user) {
        resolve(user);
      } else {
        reject(new Error('User not found'));
      }
    }, 1000);
  });
}

// 使用
fetchUserData(1)
  .then(user => {
    console.log(`Hi ${user.name}!`);
    console.log(`Your role: ${user.role}`);
  })
  .catch(error => {
    console.error('Failed to load data:', error.message);
  })
  .finally(() => {
    console.log('Request completed');
  });

Promise.allとPromise.race

JavaScriptは、多くのpromiseを操作するための便利な静的メソッドを提供します。

Promise.all

メソッド Promise.all は、promiseの配列を受け取り、配列内のすべてのpromiseが実行されたときに実行される新しいpromiseを返します。少なくとも1つの約束が拒否された場合、Promise.all全体が拒否されます。

const promise1 = Promise.resolve(3);
const promise2 = new Promise(resolve => setTimeout(() => resolve(42), 1000));
const promise3 = fetch('https://api.example.com/data').then(r => r.json());

Promise.all([promise1, promise2, promise3])
  .then(results => {
    console.log('All results:', results);
    // 結果は配列になります: [3, 42, {... APIからのデータ}]
  })
  .catch(error => {
    console.error('One of the promisses ended in error:', error);
  });

これは、いくつかの独立した非同期操作を実行し、それらすべてを待つ必要がある場合に便利です。

Promise.race

メソッド Promise.race は、最初に渡された約束が実行または拒否されるとすぐに実行または拒否される約束を返します。

const slowPromise = new Promise(resolve => 
  setTimeout(() => resolve('Slow'), 3000)
);
const fastPromise = new Promise(resolve => 
  setTimeout(() => resolve('Fast'), 1000)
);

Promise.race([slowPromise, fastPromise])
  .then(result => {
    console.log('Winner:', result); // 「クイック」});

これは、タイムアウトを実装したり、最速のデータソースを選択したりするのに役立ちます。

Promise.allSettledとPromise.any

JavaScriptの最新バージョンでは、さらに2つの便利なメソッドが追加されました。

Promise.allSettled

Promise.allとは異なり、Promise.allSettledメソッドは、結果に関係なく、すべての約束が完了するのを待ちます。各約束に関する情報を含むオブジェクトの配列を返します。

const promises = [
  Promise.resolve('Success'),
  Promise.reject('Error'),
  Promise.resolve('More success')
];

Promise.allSettled(promises)
  .then(results => {
    results.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        console.log(`Promis ${index}: executed with value ${result.value}`);
      } else {
        console.log(`Promo ${index}: rejected with reason ${result.reason}`);
      }
    });
  });

Promise.any

メソッド Promise.any は、最初に正常に実行された約束を返します。すべての約束が拒否された場合、拒否された約束が集約されたエラーとともに返されます。

const promises = [
  Promise.reject('Error 1'),
  Promise.resolve('Success'),
  Promise.reject('Error 2')
];

Promise.any(promises)
  .then(result => {
    console.log('First successful result:', result); // '成功's'
  })
  .catch(error => {
    console.error('All promissory notes are rejected:', error);
  });

約束を扱う際の典型的なミス

チェーン内の忘れられたリターン

よくある間違いの1つは、thenからのpromiseを返すことを忘れることです。これにより、チェーンが壊れます。

// 不正解
fetchUser()
  .then(user => {
    fetchPosts(user.id); // リターンを忘れました!
  })
  .then(posts => {
    // 投稿は未定義になります
    console.log(posts);
  });

// 正解
fetchUser()
  .then(user => {
    return fetchPosts(user.id); // プロミスを返します
  })
  .then(posts => {
    console.log(posts); // 投稿にデータが含まれるようになりました
  });

チェーンの代わりにネストされたプロミス

デベロッパーがプロミスのネストを作成することがあり、これによりコールバックヘルの問題に戻ります。

// 悪い - 入れ子
fetchUser()
  .then(user => {
    fetchPosts(user.id)
      .then(posts => {
        fetchComments(posts[0].id)
          .then(comments => {
            console.log(comments);
          });
      });
  });

// 良い — チェーン
fetchUser()
  .then(user => fetchPosts(user.id))
  .then(posts => fetchComments(posts[0].id))
  .then(comments => console.log(comments))
  .catch(error => console.error(error));

エラー処理の欠如

エラー処理のために常に catch を追加してください。そうしないと、エラーが見過ごされる可能性があります。

// 危険
fetchData().then(data => console.log(data));

// 安全
fetchData()
  .then(data => console.log(data))
  .catch(error => console.error('An error occurred:', error));

promiseからasync/awaitへ

Promisesは、非同期コードを同期コードにより近づけるasync/await構文の基礎を築きました。

// 約束付き
function getUserInfo() {
  return fetchUser()
    .then(user => fetchPosts(user.id))
    .then(posts => {
      return { user, posts };
    });
}

// async/awaitを使用
async function getUserInfo() {
  const user = await fetchUser();
  const posts = await fetchPosts(user.id);
  return { user, posts };
}

ただし、promisesを理解することは非常に重要です。async/awaitはpromisesの構文上の糖であり、それらの動作を知ることはJavaScriptで非同期を効果的に使用するのに役立ちます。

結論

Promisesは、JavaScriptの基本的な問題である非同期操作のエレガントな操作を解決しました。アクションのシーケンス管理、エラー処理、非同期操作の構成のための直感的なインターフェースを提供します。

覚えておくべき重要なポイント:

  • プロミスは3つの状態のいずれかにあり、一度だけそれを変更します

  • プロミスのチェーンは、コールバックのネストの問題を解決します

  • 常にcatchを介してエラーを処理する

  • 独立した操作を並行して実行するには、Promise.allを使用します

  • 正しいチェーンを構築するためにthenからのpromiseを返します

プロミスを深く理解することは、現代の非同期JavaScriptを操作し、非同期/待機に移行するための基礎です。

知識を体系化したいですか?プラットフォームで コディック プログラミングの実践コースを受講できます。 証明書 トレーニングの終わりに。

私たちの 快適なTelegramチャンネル —ここでは、役立つ資料を共有し、興味深いトピックについて話し合い、初心者の開発者がプロフェッショナルに成長するのを支援します。一緒に学ぶ方が楽しいです!

🎯先延ばしをやめよう

記事は気に入った?
実践の時間だ!

Kodikでは読むだけでなく、すぐにコードを書く。理論 + 実践 = 本当のスキル。

即座に実践
🧠AIがコードを説明
🏆修了証

登録不要 • カード不要