JavaScriptは、シングルスレッドで動作するプログラミング言語です。
つまり、一度に実行できる処理は一つだけです。しかし、JavaScriptで非同期処理を行うことで、複数の処理を同時に実行することができます。
非同期処理は、Webアプリケーションにおいて、ユーザーの入力待ちや外部APIのレスポンス待ちなど、長時間かかる処理をスムーズに処理するために欠かせない機能です。
本記事では、JavaScriptの非同期処理の仕組みと、代表的な非同期処理の手法であるコールバック関数、Promiseオブジェクト、async/await構文について説明します。
非同期処理とは
JavaScriptの非同期処理とは、複数の処理を同時に実行するための仕組みです。
通常、JavaScriptは上から下に順番に処理を実行しますが、非同期処理を行うことで、処理が長時間かかる場合でも他の処理を同時に実行することができます。
非同期処理は、以下のような場合に利用されます。
- ネットワーク通信のレスポンス待ち
- ファイル読み込みの待ち
- ユーザーの入力待ち
非同期処理を行う方法には、コールバック関数、Promiseオブジェクト、async/await構文の3つがあります。
コールバック関数
コールバック関数は、非同期処理の中で、処理が完了した後に実行される関数のことです。
非同期処理を行う関数の引数として、コールバック関数を渡すことで、非同期処理の完了後に実行する処理を指定することができます。
以下に、コールバック関数を使用した非同期処理の例を示します。
function getData(callback) {
setTimeout(() => {
const data = { name: 'supuky', age: 20 };
callback(data);
}, 1000);
}
getData((data) => {
console.log(data);
});
上記の例では、getData関数に引数として、コールバック関数を渡しています。getData関数では、1秒後にデータを返す非同期処理を行い、その結果をコールバック関数で渡しています。getData関数の呼び出し元である、getData関数に渡したコールバック関数が実行され、データがコンソールに表示されます。
コールバック関数を使った非同期処理は、コードがネストして読みづらくなりがちであり、エラーハンドリングも複雑になります。
この問題を解決するために、Promiseオブジェクトが導入されました。
Promiseオブジェクト
Promiseオブジェクトは、非同期処理の結果を表すオブジェクトであり、非同期処理が完了した後に、成功時の値または失敗時のエラーオブジェクトを渡すことができます。
Promiseオブジェクトは、状態を持っており、処理が成功したか失敗したかに応じて、それぞれfulfilled(成功)またはrejected(失敗)の状態になります。
以下は、Promiseオブジェクトを使用して、前述の例を書き換えたものです。
function getData() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
const data = "hello";
if (data) {
resolve(data);
} else {
reject("error");
}
}, 1000);
});
}
getData()
.then(function(data) {
console.log(data);
})
.catch(function(error) {
console.error(error);
});
Promiseオブジェクトを使用すると、非同期処理の完了後に.then()メソッドで成功時の処理を、.catch()メソッドで失敗時の処理を記述できます。
また、.then()メソッドをチェーンすることで、複数の非同期処理を連続して行うことができます。この方法では、コールバック関数を使った方法よりも可読性が高く、エラーハンドリングも簡単になります。
async/await構文
ES2017(ES8)で導入されたasync/await構文を使用すると、Promiseオブジェクトをより簡潔に扱うことができます。
async/await構文は、Promiseオブジェクトを返す関数の前にasyncキーワードを付け、Promiseオブジェクトを扱う部分にawaitキーワードを付けることで、非同期処理を同期的に記述することができます。
以下は、async/await構文を使用した例です。
async function getData() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
const data = "hello";
if (data) {
resolve(data);
} else {
reject("error");
}
}, 1000);
});
}
async function logData() {
try {
const data = await getData();
console.log(data);
} catch (error) {
console.error(error);
}
}
logData();
この例では、getData
関数がPromiseオブジェクトを返すように定義されています。そして、logData
関数では、await
キーワードを用いてPromiseオブジェクトの解決を待ち、その結果を変数data
に代入しています。
try-catch
文を使ってエラー処理を行っているため、Promiseオブジェクトがエラーを返した場合は、catch
ブロックが実行されます。
logData
関数を呼び出すことで、getData
関数が非同期的に実行され、その結果がコンソールに表示されます。
async/await構文は、コールバック関数やPromiseオブジェクトよりも直感的な記述ができ、非同期処理を同期的に扱うことができるため、よりシンプルなコードを書くことができます。
まとめ
JavaScriptの非同期処理について、コールバック関数、Promise、そしてasync/await構文について解説しました。
非同期処理は、処理の完了を待たずに次の処理を開始することができるため、Webアプリケーションのパフォーマンスを向上させるために欠かせない技術です。しかし、コールバック関数による非同期処理は、ネストが深くなりやすく可読性が低下し、Promiseでもわかりにくい書き方になることがあります。
そこで、async/await構文が登場しました。async/await構文を使うことで、非同期処理を同期処理のように書くことができます。async関数を定義して、その中でawaitでPromiseオブジェクトの完了を待ち、その結果を返すことができます。また、エラーハンドリングもtry-catch文を使って簡単に行うことができます。
非同期処理は、Webアプリケーションの開発において必要不可欠な技術です。コールバック関数やPromiseは初心者にとっては難解なものであるため、async/await構文を積極的に使っていくことで、可読性の高いコードを書くことができます。