本記事を書くきっかけ
実務に入る前に、Webアプリケーション開発の基礎の勉強をさせていただいたので、その知見の共有として。特に今回はChatGPTとの対話を通して、"文量多めの読み物っぽい"記事を目指しています。箇条書きより一連の文章の方が好みの方におすすめ。
asyncとawaitについて
「非同期処理」「待機する」といったキーワードはなんとなく覚えていたものの、説明ができなかった「async」と「await」。FEだったら避けては通れないよ!と先輩社員からもお声をいただきましたざっくり言うと
asyncで非同期的な処理をして、awaitでその中で待機しなければならない内容を書いている
そうです。実際に使うと、asyncの中のawaitがついている部分は完了までその後の処理が止まっているのがわかります。
ではここで早速、ChatGPTに 「サンプルコードで説明して」 とお願いしてみます。
async function fetchData() {
console.log("Start");
const result = await fetchSomeData();
console.log("Received:", result);
console.log("End");
}
console.log("A");
fetchData();
console.log("B");
実行結果は
A
Start
B
Received: <データの結果>
End
となります。
コードをよく見るのが面倒な時は、ChatGPTに 「コードで起きていることを説明して」 と言ってみます。
返答内容が以下。
この順序について説明すると、まず最初に同期的な処理である"A"が出力されます。その後、非同期関数fetchData()が呼び出され、"Start"が出力されます。次に、非同期処理であるfetchSomeData()関数が呼び出され、非同期処理が開始されます。この時点で非同期処理が完了するまで待機しましょう。待機中に、"B"が出力されます。その後、非同期処理が完了し、結果が取得されます。取得した結果がコンソールに表示されるため、"Received: <データの結果>"というメッセージが出力されます。最後に、"End"が出力されます。
ここで、
fetchData()の実行中にBが実行される→非同期処理が行われている
そして、
fetchData()の中でawaitがついているfetchSomeData()が実行される→完了まで待機している
の二点がポイントです。
awaitの必要性
asyncが付いた非同期処理では、待機中でも他の同期的な処理が進行し、非同期処理が完了した後にそれに関連する処理が行われるため、非同期処理の途中で他の処理が実行されることが特徴です。awaitキーワードは、非同期処理の完了を待つために使用されます。awaitが付いた処理は非同期処理の完了を待機するため、その行以降のコードは同期的なフローで実行されるようになります。これにより、非同期処理を直列的に記述することができます。
上記を把握するのにも、ChatGPTにまずサンプルコードを書いてもらいました。 「awaitの使い方がわかりやすいようにサンプルコードで教えて」 と言ってみます。
得られたサンプルコードです。
async function fetchData() {
console.log("Start");
const result1 = await fetchSomeData();
console.log("Received Result 1:", result1);
const result2 = await fetchSomeOtherData();
console.log("Received Result 2:", result2);
const result3 = await fetchAdditionalData();
console.log("Received Result 3:", result3);
console.log("End");
}
fetchData();
実行結果は
Start
Received Result 1: <Result 1>
Received Result 2: <Result 2>
Received Result 3: <Result 3>
End
となります。
Result1からResult3まで、順番に記述されます。
ここで、
「えっ?そんなことしなくてもコードの基本は同期処理じゃない?awaitなんてなくても、順番に処理してくれるんじゃないの?」
と思いませんか?(わたしは思いました。)
ですが、ここでfetchSomeData()で行っているようなネットワークリクエストやファイルの読み書きなどの操作は通常、非同期的な処理として実行されます。
つまり、敢えてawaitを付けてあげないと、順番には処理してくれないのです!
これが、非同期的な処理をasyncで行いつつ、その中でawaitで同期的に見える処理を行っている理由です。
Promiseとの関係
突然新しいキーワードが出てきました。
以下は、awaitについて学ぶ過程でChatGPTで生成された内容です。
awaitキーワードは、非同期処理を含む関数の呼び出しの前に使用され、その関数がPromiseを返す場合、そのPromiseの解決を待機します。awaitによって待機される非同期処理は、Promiseを返す必要があります。
試しにPromiseを使う場合を見てみましょう。
「Promiseを使う場合をサンプルコードで教えて」
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Data fetched successfully");
}, 2000);
});
}
async function process() {
console.log("Start");
const result = await fetchData();
console.log(result);
console.log("End");
}
process();
Promiseのコンストラクタには、resolveとrejectという2つのコールバック関数が渡されます。この例では、setTimeout関数を使用して2秒後に非同期処理を完了させる設定を行っています。
setTimeout関数のコールバック関数内で、resolve関数が呼び出されています。resolve関数は、Promiseを成功状態に解決するために使用されます。この例では、文字列 "Data fetched successfully" がresolve関数に渡されており、非同期処理が成功したことを示しています。
つまり、Promiseは非同期操作の完了を表現するための仕組みであり、resolve関数を呼び出すことで非同期処理の成功を示します。
要は、Promiseは非同期操作の結果を表現するためのオブジェクトのようです。
awaitキーワードを使用してPromiseの解決を待つことは、Promiseがresolve状態になるのを待つのと同義です。
awaitで待っている関数が完了した時も、実はこっそりPromiseオブジェクトが返されているのです。
Promise.all
さらに新しいフレーズを紹介します。Promise.allです。
これは、研修の開発の中で使用したけれどもわからなかったフレーズです。
今までと同様に、
「Promise.allをサンプルコードを用いて説明して」
とお願いして、まずサンプルコードを見てみましょう。
const promise1 = Promise.resolve('Result 1');
const promise2 = Promise.resolve('Result 2');
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Result 3');
}, 2000);
});
const promises = [promise1, promise2, promise3];
Promise.all(promises)
.then(results => {
console.log(results); // ['Result 1', 'Result 2', 'Result 3']
})
.catch(error => {
console.error(error);
});
Promise.allメソッドは、引数としてPromiseオブジェクトの配列を受け取ります。Promise.all(promises)は、引数で渡された全てのPromiseが解決するまで待ちます。
「全部が完了したら処理を行う」という感じですね!
最後に
長くなりましたが、これでasyncとawaitについて理解していただけたのでは?と思います。
筆者も本記事の執筆を通して理解を深めていった身なので、不足している部分があるかもしれません。そこで、最後に
「筆者が」どのようにChatGPTと対話しつつ理解を深めたか
をまとめて、終わりにしたいと思います。
- 一旦記事等で調べてみる
- わからなかった箇所をChatGPTに聞く
- 自分の理解した内容を書き、「この理解で合っていますか?」と聞く
- またわからない箇所が出たらわからないフレーズをコピペして教えてもらう
特に、「自分の理解を一旦ChatGPTに投げる」部分は、ChatGPTが間違えた内容を共有してきているケースを洗い出しやすいのでオススメです。
それでは、良いエンジニアライフを!