JavaScriptの非同期処理が全然わからなかったのですが、chatGPTと対話したりなどいろいろ調べていく間に理解が深まりました。
私と同じく雑魚エンジニアの皆さんも、一緒に一歩前に進めればと願って記事を遺します!
前提条件
- 少しでもプログラミングしたことがある
- JavaScriptの基礎知識がある
それではいきましょう!
非同期処理ってなによ?
そもそもの非同期処理について説明します。
非同期処理とは、プログラムがタスクを待つ間に、他のタスクを進めることができるような処理の仕方を指します。例えば、あなたがレストランで料理を注文したとします。料理が出来上がるまでに時間がかかるため、その間に他のお客さんの注文を取ることができる。これが非同期処理です。対照的に、同期処理は料理が出来上がるまで他の注文を取らずに待つようなものです。
では、JavaScriptの非同期処理について見てみましょう。
JavaScriptはWebブラウザで動く言語で、非同期処理をよく使います。例えば、ウェブサイトからデータを取得するためにサーバーにリクエストを送る場合、そのレスポンスを待つ間に他のタスク(ユーザーのクリックイベントなど)を処理します。
非同期処理はJavaScriptではasyncを使用します。
asyncを関数の前に書くと、その関数は非同期になります。
async function fetchSomeData() {
// 処理内容
}
asyncを使った関数はPromiseオブジェクトを返却します。
Promiseオブジェクトとは?
Promiseは、JavaScriptの特殊なオブジェクトで、未来の結果を表現します。この「未来の結果」とは、まだ処理が完了していないけれども、いずれは結果が得られるようなものです。これは「私たちはまだ結果を持っていませんが、いつかは結果を提供するという約束をします」という考え方を表しています。
プログラミングの世界における「約束」は、特に時間がかかる操作に対して使われます。例えば、サーバからデータを取得する場合、ネットワークの状況によってはすぐにはデータが手に入らないかもしれません。しかし、その操作をPromiseで表現することで、データが手に入る「約束」を作ることができます。
Promiseは以下の3つの状態のうちの1つになります:
- pending(保留):結果がまだ利用できない状態。
- fulfilled(解決):結果が利用できるようになった状態。この状態になると、Promiseの結果を取得することができます。
- rejected(拒否):何らかのエラーが起きた状態。この状態になると、Promiseは結果を提供できないことを示します。
Promiseは、JavaScriptで非同期操作を扱うための強力なツールです。特に、時間がかかる操作(ネットワーク通信やデータベースへのアクセスなど)を行う際に有用です。
Promiseオブジェクトの変化
async関数を実行すると、その関数はすぐにPromiseオブジェクトを返します。その時点でのPromiseの状態は「pending」(保留)です。
async関数の中で行われる処理が完了すると(エラーが発生しない限り)、Promiseの状態は「fulfilled」(解決)になります。その際には新たなPromiseオブジェクトが返されるわけではなく、元々返されたPromiseの状態が変わるだけです。そのPromiseが解決した(fulfilled)ときの値は、async関数のreturn文で指定された値(あるいは、return文がない場合はundefined)になります。
もしasync関数内で何らかのエラーが発生した場合、Promiseの状態は「rejected」(拒否)になります。その際のエラー情報も、元のPromiseから取得することができます。
したがって、async関数を呼び出すと、1回だけPromiseオブジェクトが返されます。そのPromiseは、初めに「pending」の状態で、後に「fulfilled」または「rejected」の状態に変わります。
以下、ソースの流れです。
async function myAsyncFunction() {
return "Hello, world!";
}
// async関数を実行
const promise = myAsyncFunction();
console.log(promise); // Promise { <pending> }
// thenはPromiseオブジェクトがfullfiledになると実行される
promise.then(result => {
console.log(result); // "Hello, world!"
console.log(promise); // Promise { <fulfilled>: 'Hello, world!' }
});
どうやってasyncの処理結果を得るのか?
asyncの処理結果を得るためには、thenとawaitの2種類が使えます。
現場では主にawaitが使用されます。より直接的に結果を取得できるからです。
ただし、awaitはasync関数の中でのみ使用可能です。
thenを使用したパターン
const promise = fetchSomeData(); // Promiseを返す関数
promise.then(data => {
console.log(data); // データが利用できるようになった時に実行される
});
thenを使用し、結果が拒否された場合を考慮したパターン
promise
.then(data => console.log(data))
.catch(error => console.log("An error occurred: " + error));
awaitを使用したパターン(try/catchを使用)
async function fetchData() {
try {
const data = await fetchSomeData(); // Promiseが解決するまで待つ
console.log(data);
} catch (error) {
console.log("An error occurred: " + error);
}
}
awaitはPromiseが解決するまで処理を一時停止しますが、他の非同期処理(例えば、他のPromise)はその間に進行します。したがって、awaitを使うことでコードは読みやすくなりますが、同時に複数の非同期処理を効率的に行うためには、適切な使い方が必要です。
終わりに
いかがでしたでしょうか?Promiseオブジェクトの理解が難しかったのですが、結果次第で処理が起動するのは面白いですね。
ここまで読んでくださりありがとうございます!