非同期処理
Asynchronous Processing
非同期処理は、処理の完了をその場で待ち続けず、結果を後で受け取る方式です。待っている間に別の作業を進められますが、CPUが複数の計算を同時実行する並列処理と同じ意味ではありません。
🐾 猫で例えると?
脚を伸ばしてひと休みするアメショ。ここから「ご飯をお願いした後、できあがるまでは毛づくろいをして、呼ばれたら食べに行く」場面を想像してみましょう。お願いが要求、準備の間が待ち時間、呼ばれて食べるのが完了後の処理です。待ち時間に別のことを進める点が、非同期処理に対応します。
💻 開発現場における「非同期処理」とは?
Webサイトやスマートフォンアプリでは、通信などの待ち時間に画面の操作を続けられるよう、非同期処理が使われます。例えばSNSでは、画像や動画の取得が終わるまで画面全体を止めず、読み込み中にもスクロールなどの操作を受け付けられるようにします。
ブラウザーの通信機能であるfetchなどは、通信を待つ間にほかの処理を進める助けになります。ただしasyncを付けた関数でも、長い計算をそのまま実行すれば画面の操作や表示も担当するメインスレッドを占有します。計算を別の実行場所へ分けるには、Web Workerなどの仕組みが必要です。
⚠️ 非同期処理の仕組みと注意点
非同期処理では、通信の状況などによって完了時刻が変わり、並行して始めた処理の完了順も入れ替わる場合があります。データが取得できていないのに画面を表示しようとしてエラー(Undefined)になるなど、特有のバグが発生しやすくなります。
// /api/cat がJSONを返す想定。APIは別途必要。
async function fetchCat() {
const response = await fetch("/api/cat");
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
}
fetchCat()
.then((cat) => console.log("取得した猫", cat))
.catch((error) => console.error("取得できませんでした", error));
console.log("通信の完了を待たずに進める処理"); awaitは、そのasync関数の続きを一時停止します。例ではHTTPの成功確認とJSONの読込を順に行い、失敗を呼び出し元で受け取ります。fetchは404や500でも通常は非同期の結果を扱うPromiseを成功状態にするため、response.okの確認を別に行っています。
🛠️ 非同期処理を賢く使うためのポイント
システムのパフォーマンスを最大化し、ユーザーにストレスを与えないためには、非同期処理の適切なコントロールが求められます。
- エラーハンドリングを必ず実装する: 通信失敗やタイムアウトなど、バックグラウンド処理が失敗した際の例外処理(try-catchなど)を漏れなく記述する必要があります。
- ローディング状態(UI)の明示: ユーザーに「裏でデータを取りに行っている」ことが伝わるよう、スピナーやプログレスバーを表示して不安を与えないUI設計が重要です。
- 独立した待ちを重ねる: Promise.allは複数のPromiseの結果をまとめて待てますが、CPU処理を自動で別コアへ分ける機能ではありません。また、一つが失敗しても他の処理が自動でキャンセルされるわけではありません。
ご飯を待つ間に毛づくろいをする例えのように、非同期処理では待ち時間に別の仕事を進められます。ただし結果が早く完成する保証ではありません。完了・失敗を受け取る処理と、待っている間の表示を用意することが、利用者が状況を把握できる画面につながります。