
変数とデータ型(let, const, プリミティブ型)
はじめに JavaScriptを学ぶ上で、変数とデータ型の理解は基礎中の基礎です。この知識なしでは効果的なプログラミングはできません。本記事では、現代のJava […]
Promiseを学んだことで、非同期処理の扱いが大幅に改善されたことを実感できたと思います。しかし、Promiseチェーンも複雑になると可読性が下がる場合があります。この章では、ES2017で導入された「async/await」について詳しく解説します。async/awaitを使うと、非同期コードを同期コードのように直感的に書けるようになり、Promiseの利点を保ちつつ、さらに可読性を高めることができます。
async/awaitはPromiseをベースにしたシンタックスシュガー(構文糖)で、非同期コードを同期コードのような見た目で書けるようにする機能です。主に以下の2つのキーワードで構成されます:
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
return data;
} catch (error) {
console.error('エラーが発生しました:', error);
throw error;
}
}
fetchData().then(data => console.log('最終データ:', data));
Promiseチェーンも強力ですが、以下のような課題がありました:
関数の前にasync
キーワードを付けることで、その関数は常にPromiseを返すようになります。
async function myAsyncFunction() {
return 42; // 自動的にPromiseでラップされる
}
// 上記は以下と同等
function myAsyncFunction() {
return Promise.resolve(42);
}
await
はasync関数内でのみ使用でき、Promiseが解決されるまで実行を一時停止します。
async function fetchUser() {
const response = await fetch('/api/user');
const user = await response.json();
return user;
}
async/awaitでは、同期処理と同じくtry/catchでエラーを捕捉できます。
async function loadData() {
try {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error('ネットワークレスポンスが正常ではありません');
}
const data = await response.json();
console.log('データ取得成功:', data);
return data;
} catch (error) {
console.error('データ取得失敗:', error);
throw error; // 呼び出し元でもエラーを処理できるように
}
}
async function processTasks() {
const result1 = await task1();
console.log('task1完了:', result1);
const result2 = await task2(result1);
console.log('task2完了:', result2);
const result3 = await task3(result2);
console.log('task3完了:', result3);
return result3;
}
processTasks().catch(error => console.error('処理失敗:', error));
async function fetchAllData() {
// 並列で開始
const [users, products, orders] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/products').then(r => r.json()),
fetch('/api/orders').then(r => r.json())
]);
console.log('すべてのデータ取得完了');
return { users, products, orders };
}
async function processItems(items) {
const results = [];
for (const item of items) {
try {
const result = await processItem(item);
results.push(result);
console.log(`処理完了: ${item.id}`);
} catch (error) {
console.error(`${item.id}の処理に失敗:`, error);
}
}
return results;
}
// 非効率な例(各awaitで待機)
async function slowExample() {
const a = await fetchA();
const b = await fetchB();
return a + b;
}
// 改善例(並列で実行)
async function fastExample() {
const [a, b] = await Promise.all([fetchA(), fetchB()]);
return a + b;
}
async function example() {
const promise = fetchData(); // awaitを忘れるとPromiseオブジェクトが返る
console.log(promise); // Promise { }
}
// モジュール内でないと使えない(ES2022以降でモジュール内なら可能)
await fetchData(); // エラー
// 正しい使い方
(async () => {
await fetchData();
})();
async/awaitはPromiseと完全に互換性があります。
async function example() {
const data = await fetchData(); // fetchData()はPromiseを返す
}
async function getUser() {
return { name: '太郎' };
}
getUser().then(user => console.log(user));
async function processWithTimeout() {
return new Promise((resolve) => {
setTimeout(async () => {
const data = await fetchData();
resolve(data);
}, 1000);
});
}
async function* asyncGenerator() {
let i = 0;
while (i < 3) {
await delay(100);
yield i++;
}
}
(async () => {
for await (const num of asyncGenerator()) {
console.log(num);
}
})();
モジュール内ではトップレベルでawaitが使えます。
// module.js
const data = await fetchData();
console.log(data);
export { data };
async/awaitについての重要なポイント:
async/awaitをマスターすることで、JavaScriptの非同期プログラミングがより自然で管理しやすいものになります。
以下のコードの実行順序と出力結果を予想してください。
console.log('スクリプト開始');
async function asyncFunc() {
console.log('asyncFunc開始');
await new Promise(resolve => setTimeout(resolve, 0));
console.log('asyncFunc内のawait後');
}
asyncFunc().then(() => console.log('asyncFunc解決'));
new Promise(resolve => {
console.log('Promise実行');
resolve();
}).then(() => console.log('Promise解決'));
console.log('スクリプト終了');
次のasync/awaitの特徴について、正しいものには○、間違っているものには×をつけてください。
以下のPromiseチェーンをasync/awaitを使って書き直してください。
function getUser(userId) {
return fetch(`/api/users/${userId}`)
.then(response => {
if (!response.ok) {
throw new Error('ユーザーが見つかりません');
}
return response.json();
})
.then(user => {
return fetch(`/api/profile/${user.profileId}`);
})
.then(response => response.json())
.catch(error => {
console.error('エラー:', error);
throw error;
});
}
スクリプト開始
asyncFunc開始
Promise実行
スクリプト終了
Promise解決
asyncFunc内のawait後
asyncFunc解決
説明:
async function getUser(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error('ユーザーが見つかりません');
}
const user = await response.json();
const profileResponse = await fetch(`/api/profile/${user.profileId}`);
return await profileResponse.json();
} catch (error) {
console.error('エラー:', error);
throw error;
}
}