ChatGPTと一緒にJavaScriptの非同期処理をマスターする

Jacasciptの非同期処理をchatGPTで学んでみる。
JavaScriptの非同期処理にはいくつかの方法があるので、chatGPTに聞いてみた。
# 1. コールバック関数（Callback Functions）:
コールバック関数は、非同期な操作が完了した後に呼び出される関数である。
典型的な例として、setTimeout関数がある。
```Javascript
console.log("処理開始");

setTimeout(function() {
  console.log("3秒経過しました");
}, 3000);

console.log("処理終了");

```
setTimeout関数によって、3秒後に指定されたコールバック関数(function(){・・・})が実行され、"3秒経過しました"と表示される。
setTimeout関数は非同期なので、"処理終了"が先に表示される。

# 2. Promise:
Promiseは非同期な操作の結果を表すオブジェクトである。非同期な処理が成功した場合にはresolve関数を呼び出し、失敗した場合にはreject関数を呼び出す。
```Javascript
console.log("処理開始");

const myPromise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve("3秒経過しました");
  }, 3000);
});

myPromise.then(function(result) {
  console.log(result);
  console.log("処理終了");
});
```

このコードでは、非同期な処理をPromiseで表現している。setTimeout関数が完了した後、resolve関数が呼び出され、その結果が.thenメソッドで取得され、表示されている。

コールバック関数よりもPromiseの方が柔軟で、ネストされたコードをフラットに表現することができる。また、複数の非同期処理を順番に実行したり、並行して実行したりすることもできる。

PromiseはJavaScriptの組み込みクラスとして実装されている。
その機能を理解するために、chatGPTに同じようなクラスを作ってもらう。


## Promiseと同等のクラスの実装
以下にchatGPTに作ってもらった、Promiseと同じように機能するクラスMyPromiseの実装例を示す：

```javascript
class MyPromise {
  constructor(executor) {
    // Promiseの状態
    this.state = 'pending';
    // Promiseが解決する値
    this.value = undefined;
    // Promiseが拒否された理由
    this.reason = undefined;
    // 成功時のコールバック関数の配列
    this.onResolvedCallbacks = [];
    // 失敗時のコールバック関数の配列
    this.onRejectedCallbacks = [];

    // 成功時の処理
    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onResolvedCallbacks.forEach(fn => fn());
      }
    }

    // 失敗時の処理
    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    }

    try {
      // executorは即時実行される
      executor(resolve, reject);
    } catch (error) {
      // エラーが発生した場合は、Promiseを拒否する
      reject(error);
    }
  }

  then(onFulfilled, onRejected) {
    // 状態がfulfilledの場合、onFulfilledを実行する
    if (this.state === 'fulfilled') {
      onFulfilled(this.value);
    }
    // 状態がrejectedの場合、onRejectedを実行する
    if (this.state === 'rejected') {
      onRejected(this.reason);
    }
    // 状態がpendingの場合、コールバック関数を保存する
    if (this.state === 'pending') {
      this.onResolvedCallbacks.push(() => { onFulfilled(this.value) });
      this.onRejectedCallbacks.push(() => { onRejected(this.reason) });
    }
  }
}
```
このクラスは、Promiseのすべての機能を網羅しているわけではないが、Promiseの非同期処理の基本的な仕組みを理解するのに役立つ。

1. コンストラクタ：
MyPromiseというクラスのコンストラクタが定義されている。コンストラクタはexecutorという関数を受け取る。executor関数は、非同期な処理を実行し、成功時にはresolve関数を呼び出し、失敗時にはreject関数を呼び出す。

2. Promiseの状態と値の管理：
MyPromiseクラス内には、Promiseの状態（state）や解決する値（value）、拒否された理由（reason）を管理するための変数がある。初期状態では、stateはpending（保留中）に設定され、valueとreasonは未定義である。

3. 成功時と失敗時の処理：
- resolve関数は、Promiseが成功した場合に呼び出される。resolve関数は、stateをfulfilled（解決済み）に変更し、渡された値をvalueに設定する。また、成功時のコールバック関数（onResolvedCallbacks）を実行する。
- reject関数は、Promiseが失敗した場合に呼び出される。reject関数は、stateをrejected（拒否済み）に変更し、渡された理由をreasonに設定する。また、失敗時のコールバック関数（onRejectedCallbacks）を実行する。

4. thenメソッド：
thenメソッドは、state（Promiseの状態）に応じて処理を行う。thenメソッドは、2つの引数、onFulfilled（成功時のコールバック関数）とonRejected（失敗時のコールバック関数）を受け取る。

- もしstateがfulfilled（解決済み）であれば、onFulfilledを実行する。
- もしstateがrejected（拒否済み）であれば、onRejectedを実行する。

- もしstateがpending（保留中）であれば、"fulfilled"状態になったときに実行すべきコールバック関数、onFulfilledとonRejectedをコールバック関数の配列に保存する。
つまり、Promiseの結果（解決値または拒否理由）がまだ利用できないときに、それらの結果が利用可能になったときに実行するためのコールバック関数を登録する。

## MyPromiseクラスの使用例
以下は、MyPromiseクラスを使用した非同期処理の例である：

まず、指定した時間後に指定した値で解決するPromiseを生成する非同期処理を行うための関数`waitFor()`を作成する。

```Javascript
function waitFor(time, value) {
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve(value);
    }, time);
  });
}
```

次に、作成したwaitFor関数を使って、1秒後に'Hello, 'という文字列、その2秒後に'world!'という文字列を出力する。


```Javascript
waitFor(1000, 'Hello, ')
  .then(value => {
    console.log(value);
    return waitFor(2000, 'world!');
  })
  .then(value => {
    console.log(value);
  });
```
thenメソッドを使用すると、非同期処理が完了した後に行う処理を記述できる。
また、thenメソッドの中で新たにMyPromiseオブジェクトを返すと、そのMyPromiseオブジェクトの処理が終わるまで次のthenメソッドの処理は待機される。
このようにして非同期処理を順番に行うことができる。

# 3. async/await:
async/awaitを使うと、非同期な処理をより直感的に書ける。
非同期な関数の前にasyncキーワードを付け、非同期な処理の前にawaitキーワードを付けることで、処理の完了を待つことができる。
例：
```Javascript
console.log("処理開始");

async function myAsyncFunction() {
  await new Promise(function(resolve) {
    setTimeout(function() {
      resolve("3秒経過しました");
    }, 3000);
  });
}

async function main() {
  await myAsyncFunction();
  console.log("処理終了");　
}

main();
```

1. console.log("処理開始");：
まず、"処理開始"というメッセージがコンソールに表示される。

2. async function myAsyncFunction() { ... }：
asyncを先頭につけることで、myAsyncFunction関数内で非同期処理を行うことを示している。

3. new Promise(function(resolve) { ... });：
新しいPromiseオブジェクトを作成している。このPromiseオブジェクトは、3秒後にresolve関数が呼び出されるまで保留状態（pending）となる。

4. setTimeout(function() { ... }, 3000);：
setTimeout関数を使用して非同期なタイマー処理を行っている。3秒後に指定されたコールバック関数resolve("3秒経過しました")が呼び出される。

5. await new Promise(function(resolve) { ... });：
awaitを使用して、非同期処理の完了を待っている。ここでは、Promiseオブジェクト（new Promise(function(resolve) { ... })）の解決を待っている。

6. async function main() { ... }：
asyncを先頭につけることで、main関数が非同期処理を行うことを示している。

7. await myAsyncFunction();：
awaitキーワードを使用して、myAsyncFunction関数の完了を待つ。ここでは、myAsyncFunctionが完了するまで次の行には進まないということを示している。

8. console.log("処理終了");：
myAsyncFunctionの処理がを完了した後に実行される。


# 結論
Javascriptの非同期処理を学びたくて、最初はchatGPTに「Promiseとは？」、「非同期処理を理解するための例を示して」などと聞いていたが、それだけでは理解するのが難しかった。

chatGPTから離れてw3schoolsというサイトのJavascriptのAsyncの章を読み、JavaScriptの非同期処理を理解することができた。Promiseの例題を見ていて、「Promiseはクラスではないのか？」と思い、ChatGPTに尋ねてみたところ、その通りだと回答があった。

そこで、chatGPTに「Promiseと同じように機能するクラスを実装してみて」と依頼してみた。
Promiseの例題とPromiseがどのように実装されているのかを学ぶことで、非同期処理がより理解しやすくなった。

ChatGPTに例題を作ってもらうだけでなく、機能の実装も教えてもらうことで、非同期処理やPromiseのような複雑な概念を理解するのがより簡単になった。