非同期処理とは?

非同期処理とは、プログラムが時間のかかる処理の完了を待っている間も、他の処理を続けられる仕組みのことです。
JavaScriptは基本的にシングルスレッドで動作するため、一度に実行できるJavaScriptの処理は1つです。しかし、サーバーとの通信やファイルの読み込みなど、完了までに時間がかかる処理もあります。
もしこれらを同期的に処理してしまうと、その間は画面の更新やユーザー操作も待たされてしまいます。そこで利用されるのが非同期処理です。非同期処理を使うことで、時間のかかる処理を待っている間も、他の処理やユーザー操作を継続できるため、快適なアプリケーションを作成できます。
同期処理と非同期処理の違い
まず、同期処理と非同期処理の違いを簡単に見てみましょう。
| 処理の種類 | 説明 |
|---|---|
| 同期処理 | 処理が一つずつ順番に実行され、前の処理が終わるまで次の処理は開始されない。 |
| 非同期処理 | 時間のかかる処理の完了を待たずに次の処理へ進み、完了したタイミングで結果を受け取る。 |
同期処理は、上から順番に実行されるため分かりやすい反面、時間のかかる処理があると、その間は他の処理が止まってしまいます。
一方、非同期処理では、時間のかかる処理を開始したあと、その完了を待たずに次の処理へ進めます。そのため、画面が固まりにくく、ユーザーが快適に操作できるアプリケーションを作成できます。
同期処理の例
console.log("処理1");
console.log("処理2");
console.log("処理3");
このコードは上から順番に実行され、次のように表示されます。
処理1
処理2
処理3
非同期処理の例
console.log("処理1");
setTimeout(() => {
console.log("処理2");
}, 2000);
console.log("処理3");
このコードでは、setTimeout()によって「処理2」は2秒後に実行されます。そのため、実際の表示順は次のようになります。
処理1
処理3
処理2
このように、非同期処理ではコードを書いた順番と実際に実行される順番が異なる場合があることを理解しておくことが重要です。
JavaScriptにおける非同期処理
JavaScriptで非同期処理を実現する主な方法には、次の3つがあります。
- コールバック関数:処理が完了したあとに実行される関数。
- Promise:非同期処理の成功・失敗を表現するオブジェクト。
- async/await:Promiseを同期処理のように分かりやすく記述するための構文。
現在では、Promiseやasync/awaitを使用するのが一般的です。コールバック関数は仕組みを理解するために重要ですが、新しいコードではasync/awaitが推奨されます。
コールバック関数
コールバック関数は、非同期処理が完了したあとに呼び出される関数です。
function fetchData(callback) {
setTimeout(() => {
console.log("データ取得完了");
callback();
}, 2000);
}
function processData() {
console.log("データ処理開始");
}
fetchData(processData);
このコードは次の流れで実行されます。
fetchData()が実行される。- 2秒後に「データ取得完了」が表示される。
callback()が実行される。processData()が呼び出され、「データ処理開始」が表示される。
コールバック関数はシンプルですが、非同期処理が増えると関数の入れ子が深くなり、コードが読みにくくなることがあります。これをコールバック地獄(Callback Hell)と呼びます。
Promise
Promiseは、非同期処理の結果を表現するオブジェクトです。成功した場合はresolve()、失敗した場合はreject()が呼び出されます。
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
if (success) {
resolve("データ取得完了");
} else {
reject("データ取得に失敗しました");
}
}, 2000);
});
}
fetchData()
.then((message) => {
console.log(message);
console.log("データ処理開始");
})
.catch((error) => {
console.error(error);
});
このコードでは、次のような流れになります。
fetchData()がPromiseを返す。- 成功すると
.then()が実行される。 - 失敗すると
.catch()が実行される。
Promiseを利用すると、コールバック関数よりも読みやすく保守しやすいコードになります。
async/await
async/awaitはPromiseをより簡潔に記述するための構文です。現在のJavaScriptでは最もよく利用される方法です。
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("データ取得完了");
}, 2000);
});
}
async function processData() {
const message = await fetchData();
console.log(message);
console.log("データ処理開始");
}
processData();
このコードでは、次のような流れになります。
fetchData()を呼び出す。awaitによってPromiseの完了を待つ。- 取得した結果を
messageへ代入する。 - 続きの処理を実行する。
awaitはPromiseが完了するまで待機しますが、その間もJavaScript全体が停止するわけではありません。他の非同期処理や画面操作は通常どおり実行されます。
async/awaitでのエラー処理
実際の開発では、通信エラーなどに備えてエラー処理も行います。
async function processData() {
try {
const message = await fetchData();
console.log(message);
} catch (error) {
console.error(error);
}
}
try...catchを使用することで、Promiseで発生したエラーも分かりやすく処理できます。
非同期処理の利点と注意点
非同期処理には多くの利点がありますが、適切に扱うための注意点もあります。
非同期処理の利点
- アプリケーションの応答性が向上する。
- 通信やファイル読み込み中も他の処理を実行できる。
- ユーザー操作を妨げにくい。
- 効率的に複数の処理を扱える。
非同期処理の注意点
- コードを書いた順番と実行順序が異なる場合がある。
- 非同期処理同士の実行タイミングを意識する必要がある。
- エラー処理を忘れると、不具合の原因が分かりにくくなる。
- 不要な
awaitを多用すると、かえって処理速度が低下することがある。
まとめ
- 非同期処理は、時間のかかる処理を待っている間も他の処理を続けられる仕組みです。
- JavaScriptでは、サーバー通信やタイマー処理などで非同期処理が広く利用されています。
- 現在はPromiseやasync/awaitを使用する書き方が主流です。
- コードの実行順序が変わる場合があるため、処理の流れを意識することが重要です。
- 実際の開発では、
catchやtry...catchによるエラー処理も必ず行いましょう。