【非同期処理】Promiseとは

## Promiseとは？
Promiseは非同期処理の結果を表現するオブジェクトの一つです。
非同期処理の最終的な完了（または失敗）およびその結果の値を表します。

### Promiseは主に3つの状態を持ちます

1. **pending（保留中）**: 初期状態。成功も失敗もしていない。
2. **fulfilled（満たされた）**: 処理が成功し、Promiseが結果を持っている状態。
3. **rejected（拒否された）**: 処理が失敗し、エラーが発生した状態。

Promiseが`fulfilled`または`rejected`のどちらかの状態になると、`settled（解決済み）`という状態となります。
一度Promiseが解決されると、その状態は変更されないことが保証されます。

## Promiseの基本的な使い方
Promiseを作成するときは、`new Promise`コンストラクタを使用します。

```javascript
const promise = new Promise((resolve, reject) => {
  // 何らかの非同期処理を実行

  if (/* 非同期処理が成功 */) {
    resolve('成功時の結果');
  } else {
    reject('エラー情報');
  }
});
```

ここでの`resolve`は非同期処理が成功したときに呼び出され、`reject`は失敗したときに呼び出されます。

#### ちなみに
`resolve` と` reject` は、Promise コンストラクタの中で使われる関数の標準的な名前ですが、それらの名前自体が特別な意味を持っているわけではありません。つまり、実際の関数名は何でも良いのです。(`aaa`,`bbb`など)

ただし、慣習として、多くの開発者が `resolve` と `reject` という名前を使用しています。

## Promiseの利点
1. **チェーン可能**: `then`を使って非同期処理の結果をチェーンして処理することができます。

```javascript
doSomethingAsync()
  .then(result => doSomethingElseAsync(result))
  .then(newResult => doThirdThing(newResult))
  .catch(error => console.error(error));
```

2. **エラーハンドリング**: `catch`メソッドを使用してエラーを簡単にキャッチできます。
3. **並列実行**: `Promise.all`を使って複数のPromiseを並行して実行することができます。

## Promiseの欠点
1. **エラーハンドリング**: コールバックよりは改善されていますが、大規模なプロジェクトや複雑な非同期処理のチェーンでは、エラーハンドリングが難しくなることがあります。
2. **読みにくい**: 複数のネストや長いチェーンが発生すると、コードが読みにくくなることがある。

このような背景から、さらなる改善として`async/await`が導入されましたが、Promiseはその基盤として非常に重要な役割を持ちます。


## オンラインショッピングの例

オンラインショッピングの状況を考慮して、非同期処理を模倣する簡単なコードです。
以下は、商品の購入、クレジットカードの確認、在庫の確認というステップをPromiseを使用して非同期に行う例です。

```javascript
// 商品を購入する関数
function purchaseProduct(product) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(`商品${product}を購入しました。`);
      resolve(product);
    }, 1000); // 1秒かけて商品を購入すると仮定
  });
}

// クレジットカード情報を確認する関数
function verifyCreditCard(cardInfo) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if(cardInfo === 'VALID_CARD') {
        console.log('クレジットカード情報が正常です。');
        resolve('CARD_VERIFIED');
      } else {
        reject('クレジットカード情報が不正です。');
      }
    }, 2000); // 2秒かけてクレジットカードを確認すると仮定
  });
}

// 在庫を確認する関数
function checkInventory(product) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(`商品${product}の在庫を確認します。`);
      resolve('IN_STOCK'); // ここでは常に在庫があると仮定
    }, 1000); // 1秒かけて在庫を確認すると仮定
  });
}

// 以下は、上記の関数を連結して非同期に実行するコードです。
purchaseProduct('Tシャツ')
  .then(product => verifyCreditCard('VALID_CARD'))
  .then(status => checkInventory('Tシャツ'))
  .then(stockStatus => console.log('購入処理が完了しました。'))
  .catch(error => console.error('購入処理中にエラーが発生:', error));
```

上記のコードは、
1.商品の購入
2.クレジットカードの確認
3.在庫の確認
という3つのステップを非同期に実行しています。

それぞれのステップは独立してバックグラウンドで実行され、完了すると次のステップへと移行します。
このようにPromiseを使うことで、非同期処理を直感的かつ順序立てて実行することができます。

## 疑問：非同期にする必要ある？purchaseProductで同期的にしてるようにも見える

ここをもう少し、わかりやすく理解していきたいと思います。

わかりました。もう少し具体的な例で詳しく説明してみます。

### 例: オンラインショッピング

あなたはオンラインショッピングのウェブサイトを使っています。
商品をカートに入れた後、購入ボタンをクリックすると、以下のステップが非同期的に実行されると想像してみましょう。

1. クレジットカード情報の確認
2. 在庫の確認
3. 送り先住所の確認
4. 最終的な購入の確定

これらのステップは順番に実行する必要があります。
クレジットカードが無効であれば、在庫や住所の確認をする必要はありません。
同様に、在庫がなければ住所の確認や購入の確定をする意味がありません。

#### 同期的な処理

同期処理の場合、指示されたタスクが完了するまで次のタスクに進むことができません。
このため、特に時間がかかるタスク（例えばクレジットカード情報の確認などの外部APIとの通信）を同期的に処理すると、その間、ユーザーは何もできなくなってしまいます。

#### 同期的な処理の具体例

1. **ページが「固まる」**：ユーザーがページの他の部分をクリックしても、何も反応しなくなります。
2. **インタラクションができない**：入力フォームやボタンなど、他のインタラクションもできなくなります。
3. **移動が制限される**：他のページへのリンクをクリックしても、移動することができません。
4. **不快なユーザー体験**：ユーザーはこのような「固まった」ページを不快に感じ、サイトを離れる可能性が高まります。

このような理由から、特にウェブのフロントエンドの開発においては、時間がかかるタスクや外部との通信処理は非同期で行われることが一般的です。これにより、ユーザーがスムーズにサイトを操作できるように保たれます。

#### 非同期的な処理
- 各ステップはバックグラウンドで行われ、ユーザーはウェブページを操作し続けることができます。
- しかし、次のステップは前のステップが完了するまで実行されません。このため、非同期処理が「同期的に見える」と言います。

#### 非同期的な処理の具体例

非同期処理が行われている間、具体的にユーザーができる操作は以下のようなものが考えられます。

1. **ページの閲覧**：非同期処理中でも他の部分のページの内容を閲覧することができます。
  
2. **情報の入力**：クレジットカード情報の確認が行われている間に、例えばギフトメッセージの入力や、他の配送オプションの選択など、他のフォームの情報を入力・編集することができます。

3. **他のページへの移動**：非同期処理がバックグラウンドで行われている間に、他のページへのリンクをクリックして移動することができます。ただし、購入処理が途中である場合、移動すると処理がキャンセルされる可能性があります。

4. **フィードバックの閲覧**：クレジットカード情報の確認が進行中であることを示すローディングインジケータや進行バーを見ることができます。

5. **その他のインタラクション**：非同期処理が行われている間に、ウェブサイトの他の機能（例えばヘルプのポップアップを開く、FAQを読む、カスタマーサポートとチャットするなど）を利用することができます。

このような動作が可能になるのは、非同期処理がメインのスレッドをブロックせずにバックグラウンドで実行されるためです。その結果、ユーザーはページの応答性が維持され、他の操作を続けることができます。