Promiseとは何か
JavaScriptのPromiseは、非同期操作の最終的な完了(または失敗)およびその結果の値を表現するオブジェクトです。Promiseは最初に「pending」状態で作成され、それは最終的に「fulfilled」または「rejected」のいずれかの状態になります。
- Fulfilled: 非同期操作が成功し、Promiseが結果を持っています。
- Rejected: 非同期操作が失敗し、何らかの理由(エラー)があります。
Promiseがfulfilledまたはrejected状態になると、それは「settled」と呼ばれ、その状態は変更されません。つまり、Promiseは一度だけ解決されます。
Promiseは非同期プログラミングをより直感的で理解しやすくするための強力な抽象化です。それは時間がかかる操作(例えば、サーバーからデータを取得する)を「約束された」値として扱うことを可能にします。この操作が完了すると、Promiseはその結果を提供し、それに対応する処理を行うことができます。これにより、非同期コードを同期コードのように直列化し、理解しやすくすることができます。また、エラーハンドリングも容易になります。
Promiseの待ち合わせの基本
JavaScriptのPromiseは非同期操作を表現するための強力なツールですが、複数のPromiseを扱う場合、それらを「待ち合わせる」方法が必要になります。これは、複数の非同期操作がすべて完了するのを待つ、または最初に完了した操作の結果を取得するといったシナリオで役立ちます。
JavaScriptには、Promiseの待ち合わせを行うためのいくつかの組み込みメソッドがあります。
-
Promise.all([promise1, promise2, …]): このメソッドは、すべてのPromiseが成功したときに成功し、いずれかのPromiseが失敗したときに失敗する新しいPromiseを返します。結果の配列は、入力のPromiseの順序と一致します。
-
Promise.race([promise1, promise2, …]): このメソッドは、最初に解決(成功または失敗)したPromiseの結果または理由を反映する新しいPromiseを返します。
これらのメソッドを使用することで、複数の非同期操作を効率的に管理し、それらが完了するのを待つことができます。これは、非同期プログラミングにおける重要なパターンであり、JavaScriptのPromiseを最大限に活用するために理解するべき基本的な概念です。次のセクションでは、これらのメソッドの具体的な使用例を見ていきましょう。
async/awaitを使ったPromiseの待ち合わせ
JavaScriptのasync/await構文は、非同期コードを書くための強力なツールであり、Promiseの待ち合わせをより直感的で読みやすい形で行うことができます。
asyncキーワードは、関数が非同期であることを示します。非同期関数は、必ずPromiseを返します。もし非同期関数が値を返す場合、その値はPromiseにラップされます。
async function foo() {
return 1;
}
foo().then(alert); // 1
awaitキーワードは、非同期関数内でのみ使用できます。これはPromiseの解決を待ちます。Promiseが解決されると、その結果の値が返されます。
async function foo() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 1000)
});
let result = await promise; // Promiseが解決するのを待つ
alert(result); // "done!"
}
foo();
これらのキーワードを使用すると、非同期コードを同期コードのように書くことができます。これにより、コードの可読性と保守性が向上します。
Promiseの待ち合わせにasync/awaitを使用する例を見てみましょう。
async function foo() {
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("promise1 done!"), 1000)
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("promise2 done!"), 2000)
});
let result1 = await promise1; // promise1が解決するのを待つ
let result2 = await promise2; // promise2が解決するのを待つ
alert(result1); // "promise1 done!"
alert(result2); // "promise2 done!"
}
foo();
この例では、promise1
とpromise2
が両方とも解決するのを待ってから、その結果を表示しています。これは、複数の非同期操作がすべて完了するのを待つ、という一般的なシナリオを表しています。このように、async/awaitはPromiseの待ち合わせを簡単に行うための強力なツールです。ただし、この方法ではPromiseが順番に解決されるため、全体の処理時間は各Promiseの処理時間の合計になります。これを改善するためには、Promise.all()
を使用すると良いでしょう。これについては次のセクションで詳しく説明します。
Promise.allとPromise.raceの使い方
JavaScriptのPromise.allとPromise.raceは、複数のPromiseを扱う際に非常に便利なメソッドです。
Promise.all
Promise.allは、配列内のすべてのPromiseが解決されるのを待つためのメソッドです。すべてのPromiseが成功した場合にのみ成功し、1つでも失敗するとすぐに失敗します。
let promise1 = Promise.resolve(3);
let promise2 = 42;
let promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values); // expected output: Array [3, 42, "foo"]
});
この例では、Promise.all
は3つのPromise(promise1
、promise2
、promise3
)がすべて解決するのを待ち、その結果を配列として返します。
Promise.race
一方、Promise.raceは、配列内のどれか1つのPromiseが解決または拒否されるのを待つためのメソッドです。最初に解決または拒否されたPromiseの結果または理由が返されます。
let promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'one');
});
let promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
// expected output: "two"
});
この例では、Promise.race
はpromise1
とpromise2
のうち最初に解決されたPromiseの結果を返します。
これらのメソッドを使用することで、複数の非同期操作を効率的に管理し、それらが完了するのを待つことができます。これは、非同期プログラミングにおける重要なパターンであり、JavaScriptのPromiseを最大限に活用するために理解するべき基本的な概念です。次のセクションでは、これらのメソッドの具体的な使用例を見ていきましょう。
複数の非同期処理を実行して結果を待つ方法
JavaScriptのPromiseを使用すると、複数の非同期処理を並行に実行し、そのすべての結果を待つことができます。これは、複数のAPIリクエストを行ったり、複数のファイルを読み込んだりするようなシナリオで非常に便利です。
以下に、複数の非同期処理を並行に実行し、すべての結果を待つ基本的なコードスニペットを示します。
// 非同期関数を定義します
async function asyncFunc(num) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(num), 1000);
});
}
// 複数の非同期処理を並行に実行します
let promise1 = asyncFunc(1);
let promise2 = asyncFunc(2);
let promise3 = asyncFunc(3);
// Promise.allを使用して、すべての非同期処理が完了するのを待ちます
let results = await Promise.all([promise1, promise2, promise3]);
console.log(results); // [1, 2, 3]
この例では、asyncFunc
関数は非同期に数値を返すPromiseを生成します。Promise.all
メソッドは、複数のPromise(この場合はpromise1
、promise2
、promise3
)を配列として受け取り、それらすべてが解決されるのを待ちます。すべてのPromiseが解決されると、Promise.all
はそれらの結果を配列として返します。
このように、JavaScriptのPromiseとasync/await構文を使用すると、複数の非同期処理を効率的に管理し、それらが完了するのを待つことができます。これは、非同期プログラミングにおける重要なパターンであり、JavaScriptのPromiseを最大限に活用するために理解するべき基本的な概念です。次のセクションでは、これらのメソッドの具体的な使用例を見ていきましょう。
Promiseの待ち合わせに関する実用的な例
JavaScriptのPromiseを使用すると、複数の非同期処理を並行に実行し、そのすべての結果を待つことができます。以下に、この概念を利用した実用的な例を示します。
複数のAPIリクエスト
Webアプリケーションでは、しばしば複数のAPIリクエストを並行に行い、すべての結果を待つ必要があります。以下に、fetch
関数を使用して複数のAPIリクエストを行い、Promise.all
を使用してすべての結果を待つ例を示します。
// 複数のURLからデータを取得します
let urls = [
'https://api.example.com/users',
'https://api.example.com/posts',
'https://api.example.com/comments'
];
// 各URLからデータを取得するPromiseを作成します
let promises = urls.map(url => fetch(url));
// Promise.allを使用して、すべての非同期処理が完了するのを待ちます
Promise.all(promises)
.then(responses => Promise.all(responses.map(r => r.json())))
.then(results => {
console.log(results); // 各URLから取得したデータの配列
});
この例では、Promise.all
は3つのPromise(各fetch
呼び出しの結果)がすべて解決するのを待ち、その結果を配列として返します。
非同期処理のタイムアウト
Promise.race
を使用すると、非同期処理にタイムアウトを設定することもできます。以下に、非同期処理が指定した時間内に完了しない場合にエラーをスローする例を示します。
// 非同期処理とタイムアウトを表す2つのPromiseを作成します
let asyncOperation = new Promise((resolve, reject) => {
// 非同期処理を模倣します
setTimeout(() => resolve('Operation completed'), 1000);
});
let timeout = new Promise((resolve, reject) => {
// タイムアウトを模倣します
setTimeout(() => reject('Operation timed out'), 2000);
});
// Promise.raceを使用して、最初に解決または拒否されたPromiseの結果を取得します
Promise.race([asyncOperation, timeout])
.then(result => console.log(result))
.catch(error => console.error(error));
この例では、Promise.race
はasyncOperation
とtimeout
のうち最初に解決または拒否されたPromiseの結果を返します。これにより、非同期処理が指定した時間内に完了しない場合にエラーをスローすることができます。
以上のように、JavaScriptのPromiseとasync/await構文を使用すると、複数の非同期処理を効率的に管理し、それらが完了するのを待つことができます。これは、非同期プログラミングにおける重要なパターンであり、JavaScriptのPromiseを最大限に活用するために理解するべき基本的な概念です。次のセクションでは、これらのメソッドの具体的な使用例を見ていきましょう。