よくわからないPromiseとかいうやつを紐解く

## はじめに
JavaScriptの非同期処理について理解するときに、避けて通れないのが「Promise」です。

今まで雰囲気で何となく実装してきていたので、今回基礎からしっかり理解します。

## Promiseとは
Promiseは非同期処理の完了、もしくは失敗を表すオブジェクトです。
処理の完了および失敗に対して、それぞれ処理を紐づけることができます。

非同期処理は結果を返すことができますが、即座に返すことができません。

その代わり、未来のある時点（処理の結果が確定し、成功/失敗が明らかになった時）で値を提供するオブジェクトを返すことで、同期処理と同じように値を返すことができるようになります。

この値を提供するオブジェクトがPromiseになります。

実際のコード例から、さらにPromiseを深堀りします。

### 補足:Promiseの状態
Promiseオブジェクトは、以下の3つの状態のいずれかを持ちます。

1. **pending（保留中）**: 初期状態。処理が完了していない状態
2. **fulfilled（成功）**: 処理が成功して完了した状態
3. **rejected（拒否）**: 処理が失敗して完了した状態

状態はpendingから始まり、fulfilledまたはrejectedに変化します。
一度確定したら状態が変更されることはありません。

実際にコードを書いていてこの状態を目にすることはありませんが、内部ではこの状態によって挙動が決まっているということは覚えておくとよいです。

## Promiseを生成する
基本的に自身でPromiseを生成することはあまりないのですが、実際に処理を書くことでより詳しく中身を理解していきます。

```javascript
function fetchUserData(userId) {
  return new Promise((resolve, reject) => {
    // APIリクエストをシミュレート
    setTimeout(() => {
      if (userId > 0) {
        // 成功: ユーザーデータを返す
        resolve({
          id: userId,
          name: `User ${userId}`,
          email: `user${userId}@example.com`,
        });
      } else {
        // 失敗: エラーを返す
        reject(new Error("Invalid user ID"));
      }
    }, 1000); // 1秒後に結果を返す
  });
}
```
`fetchUserData`という処理がPromiseを返す関数となるよう設定しました。

### コンストラクタ
Promiseの生成には`new`キーワードと**Promiseコンストラクタ**の呼び出しが必要です。

### executor関数
コンストラクタには引数として関数を設定します。
この関数は**executor関数**と呼ばれ、この関数内で非同期処理を実行します。

### resolveとreject
executor関数は引数として2つの関数を設定します。
第一引数の関数は、非同期処理が成功した場合に呼び出す関数で、慣例的に**resolve**とすることが多いです。
第二引数の関数は、非同期処理が失敗した場合に呼び出す関数で、慣例的に**reject**とすることが多いです。

### `fetchUserData`の解説
今回はexecutor関数の処理として、仮想のAPI処理を作成しています。
タイムアウトの後、ユーザーデータを返す処理を定義しました。

非同期処理の成功/失敗は`userId`の分岐で決定しています。
0より大きい場合には処理の成功として、resolveにユーザーデータのオブジェクトを設定しています。
それ以外の場合には処理の失敗として、rejectにエラーを設定しています。

### この実装からわかること
返却されるPromiseオブジェクトは、処理の成功（resolve）と失敗（reject）のどちらの状態も保持しています。
まだこの時点で処理は実行されていないので、どちらになるかはわかりません。

しかし、成功時にはresolveの引数が、失敗時にはrejectの引数が値として設定されることになります。

このように、未来のある時点で値を提供するオブジェクトがPromiseです。

## Promiseを利用する
生成したPromiseを利用する方法を改めて確認し、さらにPromiseについての理解を深めます。

```javascript
const userData = fetchUserData(1);
userData
  .then((user) => {
    console.log("ユーザーデータを取得しました:", user);
  })
  .catch((error) => {
    console.error("エラーが発生しました:", error.message);
  });
```
`userData`でPromiseオブジェクトを受け取ります。
Promiseオブジェクトには`then`、`catch`、`finally`のメソッドが用意されています。
`finally`については今回は割愛します。

### then
引数として関数を設定します。
この関数はPromiseオブジェクトがresolveしたときに実行される処理となります。
戻り値として新しいPromiseオブジェクトを返します。

また、この関数は引数としてresolveに指定した値が設定されます。

#### 補足:thenメソッドの第二引数
thenは実際には2つめの引数を設定することができます。
```javascript:引数設定の例
promise.then(onFulfilled, onRejected)
```
`onFulfilled`は上記で解説したresolveしたときに実行される処理です。
`onRejected`はrejectされたときに実行される処理となります。

これにより、1つのthenメソッドで成功と失敗の両方のケースを処理できます。
ただし、可読性の観点から、多くの場合は後述のcatchメソッドを使用してエラーハンドリングを行うことが推奨されます。

### catch
引数として関数を設定します。
この関数はPromiseオブジェクトがrejectされたときに実行される処理となります。
こちらも戻り値として新しいPromiseオブジェクトを返します。

また、この関数は引数としてrejectに指定した値が設定されます。

### この実装からわかること
実行結果の確定していない`userData`というPromiseオブジェクトに対して、rosolveしたときの処理を`then`で、rejectされたときの処理を`catch`で指定しています。

これが冒頭の[Promiseとは](#promiseとは)で触れていた
> 処理の完了および失敗に対して、それぞれ処理を紐づける

ということになります。

### 補足:Promiseの連鎖
`then`メソッドは新しいPromiseを返すため、複数の非同期処理を連鎖させることができます。

```javascript
fetchUserData(1)
  .then(user => {
    console.log("ユーザーデータを取得しました:", user);
    return fetchUserPosts(user.id);  // 新しいPromiseを返す
  })
  .then(posts => {
    console.log("ユーザーの投稿を取得しました:", posts);
    return fetchComments(posts[0].id);  // さらに新しいPromiseを返す
  })
  .then(comments => {
    console.log("最初の投稿のコメントを取得しました:", comments);
  })
  .catch(error => {
    console.error("エラーが発生しました:", error.message);
  });
```
ポイントは、必ず`then`メソッドの中で`return`を使って値を返すことです。
そうしないと、後続の処理で値を使えなくなってしまいます。

この方法により、複数の非同期操作を順序立てて実行し、各ステップの結果を次のステップで利用することができます。

なお、エラーは最後のcatchで一括して処理されます。

## まとめ
Promiseについてその実態を理解しないまま、なんとなく`then`や`chach`を見様見真似で使ってきていましたが、その挙動を紐解くことで、何をしていたのかをようやく理解することができました。

非同期処理はまだまだ苦手意識があるので、こういったところから少しずつ克服していきたいと思いました。