Async/Await と Array.map の併用
async/await
とは?
async/await
は、非同期処理を同期的に記述するための構文です。本来は非同期 (asynchronous) である処理を、あたかも同期 (synchronous) であるかのように記述できます。これにより、非同期処理をより直感的に、読みやすいコードで書くことができます。
Promise
とは?
Promise
は、非同期処理の結果 (成功か失敗) を表すオブジェクトです。非同期処理が完了すると、Promise は resolve
(成功) または reject
(失敗) の状態になり、結果を保持します。
Array.map
とは?
Array.map
は、配列の各要素に対して、指定した関数を適用し、新しい配列を生成するメソッドです。
async/await
と Array.map
の注意点
一見、async/await
を Array.map
内で使用すれば、配列の各要素に対して非同期処理を簡単に実行できそうに思えますが、そのまま使うと意図した動作になりません。
Array.map
は、内部で各要素に対して渡された関数を同期的に実行します。async/await
を使った関数は Promise を返しますが、Array.map
は Promise 自体を新しい配列の要素として扱ってしまうのです。
解決方法
async/await
を Array.map
と一緒に使うには、以下の2つの方法があります。
Promise.all
を使う
Promise.all
は、配列内のすべての Promise が解決されるのを待って、解決された値の配列を返します。
async function getUserData(userId) {
// 非同期処理 (シミュレーション)
const response = await fetch(`https://api.example.com/users/${userId}`);
return await response.json();
}
const userIds = [1, 2, 3];
async function fetchAllUserData() {
const promises = userIds.map(async (userId) => await getUserData(userId));
const userData = await Promise.all(promises);
console.log(userData); // 各ユーザーのデータが配列で取得できる
}
fetchAllUserData();
for...of
ループを使う
for...of
ループを使って、非同期処理を同期的に実行できます。
async function getUserData(userId) {
// 非同期処理 (シミュレーション)
const response = await fetch(`https://api.example.com/users/${userId}`);
return await response.json();
}
const userIds = [1, 2, 3];
async function fetchAllUserData() {
const userData = [];
for (const userId of userIds) {
const user = await getUserData(userId);
userData.push(user);
}
console.log(userData); // 各ユーザーのデータが配列で取得できる
}
fetchAllUserData();
TypeScript での注意点
TypeScript を使う場合は、async/await
を使用する関数には async
修飾子をつけ、Promise を返す関数を Promise<T>
型で型付けしましょう。
async function フェッチユーザーデータ(ユーザーID) {
// 非同期処理 (シミュレーション)
const レスポンス = await fetch(`https://api.example.com/users/${ユーザーID}`);
return await レスポンス.json();
}
const ユーザーIDリスト = [1, 2, 3];
async function 全ユーザーデータ取得() {
// 各ユーザーIDに対して非同期処理を呼び出す関数を配列に変換
const プロミス配列 = ユーザーIDリスト.map(async (ユーザーID) => await フェッチユーザーデータ(ユーザーID));
// 全ての Promise が解決されるのを待つ
const ユーザーデータ = await Promise.all(プロミス配列);
console.log(ユーザーデータ); // 各ユーザーのデータが配列で取得できる
}
全ユーザーデータ取得();
解説
- 最後に、
console.log
で取得したユーザーデータ (各ユーザーの情報) を配列として出力します。 - 全ての Promise が解決されると、
Promise.all
は解決された値の配列 (ユーザーデータ
) を返します。 - 次に
Promise.all
を使って、プロミス配列
内の全ての Promise が解決されるのを待ちます。 全ユーザーデータ取得
関数は、まずmap
を使って各ユーザーIDに対してフェッチユーザーデータ
を呼び出し、Promise を生成したものを配列 (プロミス配列
) に変換します。ユーザーIDリスト
は、取得したいユーザーのIDを配列で持っています。フェッチユーザーデータ
関数は、ユーザーIDを受け取り、非同期処理 (ここではシミュレーション) を行った後、JSON データを返します。
for...of ループを使った方法
async function フェッチユーザーデータ(ユーザーID) {
// 非同期処理 (シミュレーション)
const レスポンス = await fetch(`https://api.example.com/users/${ユーザーID}`);
return await レスポンス.json();
}
const ユーザーIDリスト = [1, 2, 3];
async function 全ユーザーデータ取得() {
const ユーザーデータ = [];
for (const ユーザーID of ユーザーIDリスト) {
// 順番に各ユーザーIDに対して非同期処理を実行
const ユーザー = await フェッチユーザーデータ(ユーザーID);
ユーザーデータ.push(ユーザー);
}
console.log(ユーザーデータ); // 各ユーザーのデータが配列で取得できる
}
全ユーザーデータ取得();
- ループが全て完了すると、取得したユーザーデータが配列
ユーザーデータ
に格納されているので、console.log
で出力します。 - ユーザーの情報が取得できたら、それを
ユーザーデータ
配列に追加します。 - ループ内で
await フェッチユーザーデータ(ユーザーID)
を呼び出し、非同期処理を待ちます。 for...of
ループを使って、ユーザーIDリスト
内の各ユーザーID を順に処理します。全ユーザーデータ取得
関数は、まず空の配列ユーザーデータ
を用意します。ユーザーIDリスト
も同様です。- この方法でも
フェッチユーザーデータ
関数は同じです。
こちらは、非同期処理の結果を待機して、一括で取得する方法です。
async function フェッチユーザーデータ(ユーザーID) {
// 非同期処理 (シミュレーション)
const レスポンス = await fetch(`https://api.example.com/users/${ユーザーID}`);
return await レスポンス.json();
}
const ユーザーIDリスト = [1, 2, 3];
async function 全ユーザーデータ取得() {
// 各ユーザーIDに対して非同期処理を呼び出す関数を配列に変換
const プロミス配列 = ユーザーIDリスト.map(async (ユーザーID) => await フェッチユーザーデータ(ユーザーID));
// 全ての Promise が解決されるのを待つ
const ユーザーデータ = await Promise.all(プロミス配列);
console.log(ユーザーデータ); // 各ユーザーのデータが配列で取得できる
}
全ユーザーデータ取得();
- 生成された Promise の配列 (
プロミス配列
) をPromise.all
に渡すと、全ての Promise が解決されるのを待ってから、解決されたユーザーデータの配列 (ユーザーデータ
) を返してくれます。 - この方法では、
map
を使って各ユーザーIDに対して非同期処理 (フェッチユーザーデータ
) を呼び出し、Promise を生成します。
async function フェッチユーザーデータ(ユーザーID) {
// 非同期処理 (シミュレーション)
const レスポンス = await fetch(`https://api.example.com/users/${ユーザーID}`);
return await レスポンス.json();
}
const ユーザーIDリスト = [1, 2, 3];
async function 全ユーザーデータ取得() {
const ユーザーデータ = [];
for (const ユーザーID of ユーザーIDリスト) {
// 順番に各ユーザーIDに対して非同期処理を実行
const ユーザー = await フェッチユーザーデータ(ユーザーID);
ユーザーデータ.push(ユーザー);
}
console.log(ユーザーデータ); // 各ユーザーのデータが配列で取得できる
}
全ユーザーデータ取得();
- ループが完了すると、
ユーザーデータ
配列には、順番に処理された各ユーザーの情報が格納されています。 - ループ内で
await フェッチユーザーデータ(ユーザーID)
を呼び出し、非同期処理を待ってから、取得したユーザーデータをユーザーデータ
配列に追加していきます。
javascript typescript promise