Jacasciptの非同期処理をchatGPTで学んでみる。
JavaScriptの非同期処理にはいくつかの方法があるので、chatGPTに聞いてみた。
1. コールバック関数(Callback Functions):
コールバック関数は、非同期な操作が完了した後に呼び出される関数である。
典型的な例として、setTimeout関数がある。
console.log("処理開始");
setTimeout(function() {
console.log("3秒経過しました");
}, 3000);
console.log("処理終了");
setTimeout関数によって、3秒後に指定されたコールバック関数(function(){・・・})が実行され、"3秒経過しました"と表示される。
setTimeout関数は非同期なので、"処理終了"が先に表示される。
2. Promise:
Promiseは非同期な操作の結果を表すオブジェクトである。非同期な処理が成功した場合にはresolve関数を呼び出し、失敗した場合にはreject関数を呼び出す。
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の実装例を示す:
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の非同期処理の基本的な仕組みを理解するのに役立つ。
-
コンストラクタ:
MyPromiseというクラスのコンストラクタが定義されている。コンストラクタはexecutorという関数を受け取る。executor関数は、非同期な処理を実行し、成功時にはresolve関数を呼び出し、失敗時にはreject関数を呼び出す。 -
Promiseの状態と値の管理:
MyPromiseクラス内には、Promiseの状態(state)や解決する値(value)、拒否された理由(reason)を管理するための変数がある。初期状態では、stateはpending(保留中)に設定され、valueとreasonは未定義である。 -
成功時と失敗時の処理:
- resolve関数は、Promiseが成功した場合に呼び出される。resolve関数は、stateをfulfilled(解決済み)に変更し、渡された値をvalueに設定する。また、成功時のコールバック関数(onResolvedCallbacks)を実行する。
- reject関数は、Promiseが失敗した場合に呼び出される。reject関数は、stateをrejected(拒否済み)に変更し、渡された理由をreasonに設定する。また、失敗時のコールバック関数(onRejectedCallbacks)を実行する。
- thenメソッド:
thenメソッドは、state(Promiseの状態)に応じて処理を行う。thenメソッドは、2つの引数、onFulfilled(成功時のコールバック関数)とonRejected(失敗時のコールバック関数)を受け取る。
-
もしstateがfulfilled(解決済み)であれば、onFulfilledを実行する。
-
もしstateがrejected(拒否済み)であれば、onRejectedを実行する。
-
もしstateがpending(保留中)であれば、"fulfilled"状態になったときに実行すべきコールバック関数、onFulfilledとonRejectedをコールバック関数の配列に保存する。
つまり、Promiseの結果(解決値または拒否理由)がまだ利用できないときに、それらの結果が利用可能になったときに実行するためのコールバック関数を登録する。
MyPromiseクラスの使用例
以下は、MyPromiseクラスを使用した非同期処理の例である:
まず、指定した時間後に指定した値で解決するPromiseを生成する非同期処理を行うための関数waitFor()
を作成する。
function waitFor(time, value) {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(value);
}, time);
});
}
次に、作成したwaitFor関数を使って、1秒後に'Hello, 'という文字列、その2秒後に'world!'という文字列を出力する。
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キーワードを付けることで、処理の完了を待つことができる。
例:
console.log("処理開始");
async function myAsyncFunction() {
await new Promise(function(resolve) {
setTimeout(function() {
resolve("3秒経過しました");
}, 3000);
});
}
async function main() {
await myAsyncFunction();
console.log("処理終了");
}
main();
-
console.log("処理開始");:
まず、"処理開始"というメッセージがコンソールに表示される。 -
async function myAsyncFunction() { ... }:
asyncを先頭につけることで、myAsyncFunction関数内で非同期処理を行うことを示している。 -
new Promise(function(resolve) { ... });:
新しいPromiseオブジェクトを作成している。このPromiseオブジェクトは、3秒後にresolve関数が呼び出されるまで保留状態(pending)となる。 -
setTimeout(function() { ... }, 3000);:
setTimeout関数を使用して非同期なタイマー処理を行っている。3秒後に指定されたコールバック関数resolve("3秒経過しました")が呼び出される。 -
await new Promise(function(resolve) { ... });:
awaitを使用して、非同期処理の完了を待っている。ここでは、Promiseオブジェクト(new Promise(function(resolve) { ... }))の解決を待っている。 -
async function main() { ... }:
asyncを先頭につけることで、main関数が非同期処理を行うことを示している。 -
await myAsyncFunction();:
awaitキーワードを使用して、myAsyncFunction関数の完了を待つ。ここでは、myAsyncFunctionが完了するまで次の行には進まないということを示している。 -
console.log("処理終了");:
myAsyncFunctionの処理がを完了した後に実行される。
結論
Javascriptの非同期処理を学びたくて、最初はchatGPTに「Promiseとは?」、「非同期処理を理解するための例を示して」などと聞いていたが、それだけでは理解するのが難しかった。
chatGPTから離れてw3schoolsというサイトのJavascriptのAsyncの章を読み、JavaScriptの非同期処理を理解することができた。Promiseの例題を見ていて、「Promiseはクラスではないのか?」と思い、ChatGPTに尋ねてみたところ、その通りだと回答があった。
そこで、chatGPTに「Promiseと同じように機能するクラスを実装してみて」と依頼してみた。
Promiseの例題とPromiseがどのように実装されているのかを学ぶことで、非同期処理がより理解しやすくなった。
ChatGPTに例題を作ってもらうだけでなく、機能の実装も教えてもらうことで、非同期処理やPromiseのような複雑な概念を理解するのがより簡単になった。