JavaScriptにおける非同期処理と同期処理
JavaScriptはシングルスレッドの言語であり、一度に一つのタスクしか処理できません。しかし、非同期処理を利用することで、時間のかかるタスクをバックグラウンドで実行し、その完了を待つことなく他のタスクを進めることができます。これにより、ウェブページがフリーズすることなく、ユーザーに快適な体験を提供することが可能になります。
同期処理
同期処理は、コードが上から下へと一行ずつ順番に実行され、一つのタスクが完了するまで次のタスクは開始されません。これは、コードの読みやすさや理解しやすさに寄与しますが、時間のかかるタスク(例えば、大量のデータをフェッチするネットワークリクエスト)がある場合、そのタスクが完了するまで他のタスクがブロックされてしまいます。
console.log('Start');
let result = database.query('SELECT * FROM huge_table'); // 時間がかかるタスク
console.log('Result', result);
console.log('End');
上記のコードでは、データベースクエリが完了するまで、その後のconsole.log('End')
は実行されません。
非同期処理
一方、非同期処理では、時間のかかるタスクをバックグラウンドで実行し、そのタスクが完了したときに何をすべきかを指示するコールバック関数を登録します。これにより、時間のかかるタスクが完了するのを待つことなく、他のタスクをすぐに実行することができます。
console.log('Start');
database.query('SELECT * FROM huge_table', function(result) {
console.log('Result', result);
});
console.log('End');
このコードでは、データベースクエリがバックグラウンドで実行され、その結果を処理するコールバック関数が登録されます。そのため、console.log('End')
はすぐに実行され、ユーザーはフリーズすることなく他の操作を続けることができます。
JavaScriptの非同期処理は、コールバック関数だけでなく、Promiseやasync/awaitといった機能を利用してより直感的に、または効率的に書くことができます。これらの詳細については、次のセクションで説明します。
非同期処理の完了を待つ方法1|promiseを使う
JavaScriptのPromiseは、非同期処理の結果を表現するためのオブジェクトです。Promiseは、非同期処理が成功した場合と失敗した場合の2つの結果を持つことができます。これらはそれぞれ「resolve」(解決)と「reject」(拒否)と呼ばれます。
Promiseを作成するには、new Promise()
コンストラクタを使用します。このコンストラクタは、引数として実行関数を取ります。実行関数は、2つの引数、resolve
とreject
を取ります。
let promise = new Promise((resolve, reject) => {
// 非同期処理
});
非同期処理が成功した場合はresolve
を、失敗した場合はreject
を呼び出します。これらの関数は、それぞれPromiseの「解決値」または「拒否理由」を引数として取ります。
let promise = new Promise((resolve, reject) => {
let success = true; // 非同期処理の結果を模擬
if (success) {
resolve('成功!');
} else {
reject('失敗...');
}
});
Promiseの状態は、作成時には「pending」(未決)で、resolve
またはreject
が呼び出されると「fulfilled」(解決済み)または「rejected」(拒否済み)になります。
Promiseの結果を取得するには、.then()
メソッドを使用します。このメソッドは、Promiseが解決したときに呼び出されるコールバック関数を登録します。また、.catch()
メソッドを使用して、Promiseが拒否されたときに呼び出されるコールバック関数を登録することもできます。
promise
.then(result => {
console.log(result); // '成功!'
})
.catch(error => {
console.error(error); // '失敗...'
});
このように、Promiseを使用すると、非同期処理の完了を待つことができます。また、Promiseはチェーン可能であり、複数の非同期処理を順番に実行することも可能です。これについては、次のセクションで詳しく説明します。
非同期処理の完了を待つ方法2|async/awaitを使う
JavaScriptのasync
とawait
は、非同期処理をより直感的に書くための構文です。これらを使用すると、非同期処理をあたかも同期処理のように書くことができます。
async
は関数の前に置くキーワードで、その関数が必ずPromiseを返すことを示します。await
は非同期処理の結果を待つためのキーワードで、async
関数の中でのみ使用できます。
以下に、async
とawait
を使用した非同期処理の例を示します。
async function fetchAndLog() {
let response = await fetch('https://api.example.com/data'); // 非同期処理
let data = await response.json(); // 非同期処理
console.log(data);
}
fetchAndLog();
このコードでは、fetchAndLog
関数はasync
キーワードにより非同期関数となり、Promiseを返します。fetch
関数とresponse.json
メソッドは非同期処理を行うため、その結果を待つにはawait
キーワードを使用します。
await
キーワードにより、非同期処理の結果が得られるまで次の行の実行を一時停止します。しかし、これはそのasync
関数の中だけであり、他のタスクがブロックされることはありません。
async
とawait
を使用すると、非同期処理を同期処理のように直列的に書くことができます。これにより、コードが読みやすくなり、エラーハンドリングも容易になります。ただし、これらのキーワードはPromiseに基づいているため、Promiseの動作を理解することが重要です。これについては、次のセクションで詳しく説明します。
Promiseの状態とその扱い方
JavaScriptのPromiseは、非同期処理の結果を表現するためのオブジェクトで、以下の3つの状態を持つことができます。
- pending(未決): 初期状態で、非同期処理がまだ完了していない状態を指します。
- fulfilled(解決済み): 非同期処理が成功し、Promiseが結果を持つ状態を指します。
- rejected(拒否済み): 非同期処理が失敗し、Promiseがエラーを持つ状態を指します。
Promiseは、これらの状態を一度だけ変更できます。つまり、一度fulfilled
またはrejected
になったPromiseの状態は、それ以降変更することはできません。
Promiseの状態に基づいて処理を行うためには、.then()
、.catch()
、.finally()
といったメソッドを使用します。
.then()
メソッドは、Promiseがfulfilled
になったときに実行するコールバック関数を登録します。このメソッドは新しいPromiseを返し、そのPromiseは元のPromiseの結果に基づいてfulfilled
またはrejected
になります。
let promise = Promise.resolve('成功!'); // 解決済みのPromiseを作成
promise.then(result => {
console.log(result); // '成功!'
});
.catch()
メソッドは、Promiseがrejected
になったときに実行するコールバック関数を登録します。このメソッドも新しいPromiseを返します。
let promise = Promise.reject('失敗...'); // 拒否済みのPromiseを作成
promise.catch(error => {
console.error(error); // '失敗...'
});
.finally()
メソッドは、Promiseがfulfilled
でもrejected
でも実行するコールバック関数を登録します。このメソッドは新しいPromiseを返し、そのPromiseは元のPromiseと同じ状態になります。
let promise = Promise.resolve('成功!'); // 解決済みのPromiseを作成
promise.finally(() => {
console.log('完了'); // '完了'
});
これらのメソッドを使用することで、Promiseの状態に基づいて適切な処理を行うことができます。また、これらのメソッドはチェーン可能であり、複数の非同期処理を順番に実行することも可能です。これについては、次のセクションで詳しく説明します。
非同期処理の完了を待つ具体的なコード例
以下に、JavaScriptのPromiseとasync/awaitを使用した非同期処理の完了を待つ具体的なコード例を示します。
Promiseを使用した例
// 非同期処理を模擬する関数
function asyncTask() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('タスク完了!');
}, 1000);
});
}
// 非同期処理の完了を待つ
asyncTask()
.then(result => {
console.log(result); // 'タスク完了!'
})
.catch(error => {
console.error(error);
});
このコードでは、asyncTask
関数は非同期処理を模擬し、1秒後にPromiseを解決します。.then()
メソッドを使用して、非同期処理の結果を待ち、その結果をログに出力します。
async/awaitを使用した例
// 非同期処理を模擬する関数
function asyncTask() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('タスク完了!');
}, 1000);
});
}
// 非同期処理の完了を待つ
async function waitForTask() {
try {
let result = await asyncTask();
console.log(result); // 'タスク完了!'
} catch (error) {
console.error(error);
}
}
waitForTask();
このコードでは、asyncTask
関数は非同期処理を模擬し、1秒後にPromiseを解決します。waitForTask
関数はasync
関数で、await
キーワードを使用して非同期処理の結果を待ち、その結果をログに出力します。エラーハンドリングはtry/catch
ブロックを使用して行います。
これらのコード例は、JavaScriptの非同期処理の完了を待つ基本的な方法を示しています。非同期処理はJavaScriptの重要な特性であり、これらの概念を理解することは、効率的なコードを書くために重要です。次のセクションでは、Promiseとasync/awaitの違いについて詳しく説明します。
Promiseとasync/awaitの違いとは?
JavaScriptの非同期処理を扱うための2つの主要な概念、Promiseとasync/awaitには、以下のような違いがあります。
Promise
- Promiseは、非同期処理の結果を表現するオブジェクトです。
- Promiseは、非同期処理が成功した場合(fulfilled)と失敗した場合(rejected)の2つの状態を持つことができます。
- Promiseの結果を取得するためには、
.then()
メソッドを使用します。また、エラーハンドリングには.catch()
メソッドを使用します。 - Promiseはチェーン可能であり、複数の非同期処理を順番に実行することができます。
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
async/await
- async/awaitは、非同期処理をより直感的に書くための構文です。
async
キーワードを関数の前に置くと、その関数は必ずPromiseを返します。await
キーワードを非同期処理の前に置くと、その非同期処理の結果が得られるまで次の行の実行を一時停止します。- async/awaitを使用すると、非同期処理を同期処理のように直列的に書くことができます。これにより、コードが読みやすくなり、エラーハンドリングも容易になります。
async function fetchData() {
try {
let response = await fetch('https://api.example.com/data');
let data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
fetchData();
これらの違いを理解することで、非同期処理を効果的に扱うことができます。次のセクションでは、JavaScript Promiseのベストプラクティスについて詳しく説明します。
JavaScript Promiseのベストプラクティス
JavaScriptのPromiseは非同期処理を扱うための強力なツールですが、その力を最大限に引き出すためには、以下のようなベストプラクティスを守ることが重要です。
1. エラーハンドリングを忘れない
Promiseがrejected
になったときには、適切なエラーハンドリングを行うことが重要です。.catch()
メソッドを使用して、Promiseが拒否されたときに呼び出されるコールバック関数を登録します。
fetch('https://api.example.com/data')
.then(response => response.json())
.catch(error => console.error('エラー:', error));
2. Promiseチェーンを利用する
Promiseはチェーン可能であり、複数の非同期処理を順番に実行することができます。これにより、コードが読みやすくなり、非同期処理の流れを一目で理解することができます。
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('エラー:', error));
3. async/awaitを活用する
async
とawait
を使用すると、非同期処理を同期処理のように直列的に書くことができます。これにより、コードが読みやすくなり、エラーハンドリングも容易になります。
async function fetchData() {
try {
let response = await fetch('https://api.example.com/data');
let data = await response.json();
console.log(data);
} catch (error) {
console.error('エラー:', error);
}
}
fetchData();
4. Promise.allを使う
複数の非同期処理を並行して実行し、すべての処理が完了するのを待つ場合には、Promise.all
を使用します。
let promise1 = fetch('https://api.example.com/data1').then(response => response.json());
let promise2 = fetch('https://api.example.com/data2').then(response => response.json());
Promise.all([promise1, promise2])
.then(([data1, data2]) => {
console.log(data1, data2);
})
.catch(error => console.error('エラー:', error));
これらのベストプラクティスを守ることで、JavaScriptのPromiseを効果的に使用し、非同期処理をより効率的に扱うことができます。次のセクションでは、JavaScript Promiseの詳細な使い方について詳しく説明します。