Promiseをシミュレートして理解する

## Promiseとは
MDN曰く「**プロミス (Promise) は、非同期処理の最終的な完了もしくは失敗を表すオブジェクトです。**」
正直、何言ってるのか理解できなかったです。
でも、ChatGPTさんと問答してテストコードを弄りまくった結果、なんとか理解したと思います。
Promiseの解説記事は巷に溢れていますが、使い方が主で、 **Promise**　とは、いったいどんな物であるかという解説はほとんど見かけません。そこで、ChatGPTさんに「**Promiseオブジェクトの実態はどんなものですか？**」という質問をしたところPromiseをシミュレートしたコードを表示してくれました。
問答とテストを繰り返し最終的にできたコードは以下のようなものです。
```javascript:MyPromise.js

class MyPromise {
    constructor(executor) {
        this.state = "pending";
        this.value = undefined;
        this.handlers = [];
        this.catchHandlers = [];

        const resolve = (value) => {
            if (this.state !== "pending") return;
            this.state = "fulfilled";
            this.value = value;
            this.handlers.forEach(handler => queueMicrotask(() => handler(value)));
        };

        const reject = (error) => {
            if (this.state !== "pending") return;
            this.state = "rejected";
            this.value = error;
            if (this.catchHandlers.length > 0) {
                this.catchHandlers.forEach(handler => queueMicrotask(() => handler(error)));
            } else {
                // もし catch がない場合でも、次の then で処理できるようにする
                this.handlers.forEach(handler => queueMicrotask(() => handler(undefined, error)));
            }
        };

        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }

    then(onFulfilled, onRejected) {
        return new MyPromise((resolve, reject) => {
            const handler = (value, error) => {
                if (error) {
                    if (onRejected) {
                        try {
                            const result = onRejected(error);
                            resolve(result);
                        } catch (e) {
                            reject(e);
                        }
                    } else {
                        reject(error); // エラーハンドリングされていない場合は次の catch に渡す
                    }
                    return;
                }

                try {
                    const result = onFulfilled(value);
                    resolve(result);
                } catch (e) {
                    reject(e);
                }
            };

            this.handlers.push(handler);

            if (this.state === "fulfilled") {
                queueMicrotask(() => handler(this.value));
            } else if (this.state === "rejected") {
                queueMicrotask(() => handler(undefined, this.value));
            }
        });
    }

    catch(onRejected) {
        return new MyPromise((resolve, reject) => {
            const handler = (error) => {
                try {
                    const result = onRejected(error);
                    resolve(result);
                } catch (e) {
                    reject(e);
                }
            };

            this.catchHandlers.push(handler);

            if (this.state === "rejected") {
                queueMicrotask(() => handler(this.value));
            }
        });
    }
}

```

とてもよくできたコードでPromiseの基本的な動作を問題なくシミュレートしてくれます。でも、このままだと理解が難しいので、簡略化したコードを次に示します。
```javascript
class MyPromise {
    constructor(executor) {
        this.state = "pending";
        this.value = undefined;
        this.handlers = [];

        const resolve = (value) => {
            if (this.state !== "pending") return;
            
            this.state = "fulfilled";
            this.value = value;            
            this.handlers.forEach((handler) => {
                queueMicrotask(()=>{handler(value)})
                });
        };

        const reject = (error) => {
            if (this.state !== "pending") return;
            this.state = "rejected";
            this.value = error;
            this.handlers.forEach((handler) => {
                queueMicrotask(()=>{handler(value)})
                });
        };

        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }

    then(onFulfilled) {
        return new MyPromise((resolve_p) => {
            this.handlers.push((value_p) => {
                const result = onFulfilled(value_p);
                resolve_p(result);
            });
        });
    }
}

```
`` reject ``の部分のコードは正しくないので、`` reject ``という関数が定義されていると思ってください。

## promiseはPromiseクラスのインスタンス
Promiseに基本的な使い方は
```javascript
new MyPromise((resolve)=>{
    setTimeout(()=>{
        resolve('タイマー終了');
    },1000);
}).then((value)=>{
    console.log(value);
});
```
と使いますので、PromiseクラスにnewしてPromiseのインスタンスを作ることで利用します。
すなわち、promiseはインスタンスオブジェクトなわけです。
オブジェクトなのに、なんらかの処理を実行しているというのはなんとなく釈然としないものがありますが、それは次の仕組みによります。

## promiseはnewで即座に実行される

promiseを初めてみた時、``new Promise``とインスタンスを作るだけで処理が実行されるのが不思議でした。でも、`` MyPromise ``のコードを見れば明白です。
`` MyPromise ``のコンストラクタは`` executor ``という関数を引数にとり、コンストラクタの最後で、その関数を実行します。（``try``のところ）
`` executor ``は、Promiseをnewする時に渡した関数です。上記の例であれば
```javascript
(resolve)=>{
    setTimeout(()=>{
        resolve('タイマー終了');
    },1000);
```
です。
ですので、``new Promise``をするとコンストラクタが実行され、渡された関数が即座に実行されるので、``new Promise``だけで実行が開始されるのです。
渡された関数はこの例ではsetTimeoutを使った非同期の関数ですが、通常の同期的な関数でも、即座に``resolve``するような関数でも構いません。
promiseの非同期的な動作は、ここではなく、後述するように``resolve``の処理の中で行われます。

## executorの引数は関数
`` executor ``を実行する時の引数は`` resolve ``と`` reject ``です。
この２つの関数はPromiseクラスのコンストラクタの中で宣言されています。
上記の例であれば、`` executor ``は
```javascript
(resolve)=>{
    setTimeout(()=>{
        resolve('タイマー終了');
    },1000);
```
ですので、タイマーが１秒経過に実行されるコールバック関数
```javascript
()=>{
        resolve('タイマー終了');
    }
```
の中の`` resolve() ``はpromiseインスタンスの中の`` resolve ``メソッドです。
理由は、`` resolve() ``は、外側の`` excuter ``の引数なので、レキシカルスコープ により内側のコールバック関数内の`` resolve() ``は、この引数の`` resolve ``になります。

```javascript
const resolve = (value) => {
            if (this.state !== "pending") return;
            
            this.state = "fulfilled";
            this.value = value;            
            this.handlers.forEach((handler) => {
                queueMicrotask(()=>{handler(value)})
                });
        };
```
`` resolve() ``は基本的には、`` excuter ``の最後に実行されるメソッドです。

## resolveはthenで登録された関数を実行する

`` resolve() ``の中身をみていきましょう。

`` resolve() ``の中の``this``は、``new Promise``でできたインスタンスオブジェクトですね。
最初の処理は、promiseの**状態**（state）をチェックして**保留中**（pending）であれば**成功**（fulfilled）にします。
**保留中**（pending）以外の状態では状態は変化しないようにreturnして、続く処理はパスされます。

次は、``resolve``の引数の``value``をプロパティに保存します。`` excuter ``からの返り値は、このように``return``ではなく``resolve``を実行することで返します。

次に、一番大事なことをします。
```javascript
this.handlers.forEach((handler) => {
    queueMicrotask(()=>{handler(value)})
});
```

``hanlers``配列に登録されたハンドラー（関数）を先ほど記録したvalueを引数に実行するようにした**アロー関数**を作って、**マイクロタスクキュー**に登録します。
マイクロタスクキューに登録されたこの関数は、この``resolve``の直後に実行が開始されます。（マイクロタスクキューに他のタスクが登録されていなければ）
マイクロタスクキューに関数が登録されるので、``resolve``実行のあとは一旦実行が途切れて、強制的に**非同期処理**となります。

この``hanlers``配列に登録されたハンドラー（関数）は、実は**then**によって登録されたコールバック関数を実行するための関数です。（コールバック関数そのものではありません。）

``promise``が実行され、解決された際に``then``の後のコードが実行されるのは、この仕組みによります。

## thenはPromiseクラスのメソッド

プロミスの記述は
```javascript
new Promise(...)
.then(...)
.then(...)
.catch(...)
```
という感じに、一見すると``if 〜 else 〜``のような制御構文のように見えますが、実態はPromiseクラスのメソッドチェーンです。
new Promiseやthenメソッドが新しいpromiseを返すので、このようなメソッドチェーンが成立します。次のように書き換えると構造がわかりやすくなります。
```javascript
const p1 = new Promise(...);
const p2 = p1.then(...);
const p3 = p2.then(...);
const P4 = p3.catch(...);
```

thenメソッドは
```javascript
then(onFulfilled) {
        return new MyPromise((resolve_p) => {
            this.handlers.push((value_p) => {
                const result = onFulfilled(value_p);
                resolve_p(result);
            });
        });
    }
```
``onFulFilled``という関数を引数にとるメソッドです。

## thenは次に実行する関数を登録する

``onFulFilled``関数は、
```javascript
new MyPromise((resolve)=>{
    setTimeout(()=>{
        resolve('タイマー終了');
    },1000);
}).then((value)=>{
    console.log(value);
});
```
このようなコードであれば、
```javascript
(value)=>{console.log(value);}
```
です。
promiseが解決後に実行される関数です。
``then``メソッドは、この``onFulFilled``関数を
```javascript
(value_p) => {
    const result = onFulfilled(value_p);
    resolve_p(result);
}
```
``onFulFilled``関数実行後に``resolve_p``関数を実行する関数に包んで、``this.handlers``配列に登録します。
```diff_javascript
this.handlers.push(
+    (value_p) => {
+        const result = onFulfilled(value_p);
+        resolve_p(result);
+    }
);
```

``this.handlers``配列は、先に説明した``result``メソッドの中で、マイクロタスクキューに登録する関数を収めたものですので、キューに登録するのは、この``onFulFilled``関数を包んだ関数ということになります。
この配列のハンドラーを登録する処理は
```diff_javascript
(resolve_p) => {
+    this.handlers.push(
+        (value_p) => {
+            const result = onFulfilled(value_p);
+            resolve_p(result);
+        }
+    );
}
```
もう一つの関数に包みます。
マイクロタスクキューに登録した関数の中の``resolve_p``関数は、この包んだ関数の引数です。
```diff_javascript
return new MyPromise(
+    (resolve_p) => {
+        this.handlers.push(
+            (value_p) => {
+                const result = onFulfilled(value_p);
+                resolve_p(result);
+            }
+        );
+    }
);
```       
thenメソッドは、この関数を``executor``とした新しいpromiseインスタンスを生成してthenメソッドの返り値として戻します。
``executor``はpromiseインスタンスの生成と同時に実行されますので、結果として、thenの後に実行される処理がpromiseに中の次に実行すべきリスト（``handlers``配列）に登録されることになります。

## thenメソッドチェーン（promiseチェーン）
```javascript
then(onFulfilled) {
    return new MyPromise(
        (resolve_p) => {
            this.handlers.push(
                (value_p) => {
                    const result = onFulfilled(value_p);
                    resolve_p(result);
                }
            );
        }
    );
}
```
thenメソッドはこのようなコードになってます。
``handlers``配列は``this.handlers``となってます。
この時の``this``は、レキシカルスコープですので、thisで新しく作られたpromiseインスタンスではなく、thenが実行されている最初の（ベースとなる）promiseインスタンスです。
一方、promiseが解決された時に実行される``onFulfilled``関数を実行した**後**で実行される``resolve_p(result)``関数は、thenで新しく生成されたpromiseインスタンスの``resove``関数です。
今のpromiseのthenの関数の実行 → 新しいpromiseのresoveの実行　と進むため、

```javascript
new Promise(fx1)
.then(fx2)
.then(fx3)
.then(fx4)
```
というメソッドチェーンを説明のために分解して
```javascript
const p1 = new Promise(fx1);
const p2 = p1.then(fx2);
const p3 = p2.then(fx3);
const p4 = p3.then(fx4);
```
というコードで説明すると


> fx1実行 → p1のresolve実行 → p1のhandlers配列の中のハンドラ（fx2）の実行 → p2のresolve実行 → p2のhandlers配列の中のハンドラ（fx3）の実行 → p3のresolve実行 → p3のhandlers配列の中のハンドラ（fx4）の実行


というように処理が次々に実行されます。これがthenメソッドチェーンが恙なく実行される理由です。

## thenの処理が先行する

上記のコードの処理の順番をより詳細にみていくと、

>fx1実行（非同期） → p1.then(fx2)の実行（fx2をhandlers配列に登録） → p2.then(fx3)の実行（fx3をhandlers配列に登録） → p3.then(fx4)の実行（fx4をhandlers配列に登録）
　
→ (fx1のコールバック関数の実行） → p1のresolve実行 → p1のhandlers配列の中のハンドラ（fx2）をマイクロタスクキューに登録（ここで非同期）
→ マイクロタスクキューのfx2の実行 → p2のresolve実行 → p2のhandlers配列の中のハンドラ（fx3）をマイクロタスクキューに登録（ここで非同期）
→ マイクロタスクキューのfx3の実行 → p3のresolve実行 → p3のhandlers配列の中のハンドラ（fx4）をマイクロタスクキューに登録（ここで非同期）
→ マイクロタスクキューのfx4の実行

という感じにコードの処理自体は同期的に一気に処理され、その後に登録された処理が順次実行されるようになります。

この簡略化されたMyPromiseクラスではfx1が同期的な処理であった場合は正しく処理されませんが、冒頭のMyPromiseクラスであればfx1が同期的な処理であってもthenの処理が先に同期的に処理されます。この場合は次のようになります。

>fx1実行（同期） → p1のresolve実行 → p1.then(fx2)の実行（fx2をhandlers配列に登録） → p1のhandlers配列の中のハンドラ（fx2）をマイクロタスクキューに登録 → p2.then(fx3)の実行（fx3をhandlers配列に登録） → p3.then(fx4)の実行（fx4をhandlers配列に登録）
→ （ここで非同期）
　
→ マイクロタスクキューのfx2の実行 → p2のresolve実行 → p2のhandlers配列の中のハンドラ（fx3）をマイクロタスクキューに登録（ここで非同期）
→ マイクロタスクキューのfx3の実行 → p3のresolve実行 → p3のhandlers配列の中のハンドラ（fx4）をマイクロタスクキューに登録（ここで非同期）
→ マイクロタスクキューのfx4の実行

このように、最初に一連のpromiseインスタンスを生成したのちに、promiseに登録された処理を非同期にかつ順番通りに処理していくことになります。


## Promiseとは

newした単体のプロミス (promise) は、確かにMDNがいう「**プロミス (Promise) は、非同期処理の最終的な完了もしくは失敗を表すオブジェクト**」ではありますが、``then``メソッドを追加した場合には、

「**プロミス（promise）とは、（非）同期処理の最終的な完了もしくは失敗を表し、その結果に応じた処理を非同期に実行し、さらに続いて処理すべきプロミスへの参照を保持したオブジェクト**」という感じでしょうか。

同期的な処理のように書かれたソースプログラムを、promiseチェーンという形でメモリ上にコピーして、非同期に順序通りに実行する仕組みがPromiseの本質です。