5. Async/Await
async/await構文の基本概念と使い方を学びます。Promiseをより読みやすく書く方法、エラーハンドリング、並列処理の実装方法を実践的に学習し、現代的なJavaScriptの非同期処理をマスターします。
この章で学べること
- async/awaitとは何かを理解する
- Promiseとasync/awaitの違いを学ぶ
- async関数とawaitキーワードの使い方を習得する
- try-catch文を使ったエラーハンドリングを学ぶ
- awaitを使ってはいけない場面を理解する
- 実践的な演習を通じてasync/awaitを習得する
Async/Awaitとは?
async/awaitは、Promiseをより読みやすく書くための構文です。
Promiseのthen/catchを、まるで同期的なコードのように書くことができます。これにより、非同期処理のコードがより理解しやすくなります。
従来のPromiseとAsync/Awaitの比較
まず、同じ処理をPromiseとasync/awaitで書いた例を見てみましょう。
Promiseを使った場合
function fetchUserData() {
return fetch('/api/user')
.then(response => response.json())
.then(data => {
return data;
})
.catch(error => {
console.error('エラー:', error);
});
}
async/awaitを使った場合
async function fetchUserData() {
try {
const response = await fetch('/api/user');
const data = await response.json();
return data;
} catch (error) {
console.error('エラー:', error);
}
}
比較のポイント
- 可読性: async/awaitの方が同期的なコードのように読みやすい
- エラーハンドリング: try-catch文で一括してエラーを処理できる
- ネスト: 深いネストを避けることができる
Async/Awaitの基本構文
async関数の宣言
async/awaitを使うには、まず関数をasyncキーワードで宣言する必要があります。
async function myAsyncFunction() {
// 非同期処理をここに書く
}
awaitキーワード
awaitキーワードは、Promiseが完了するまで待機します。awaitはasync関数内でのみ使用できます。
async function example() {
const result = await somePromise;
console.log(result);
}
基本的な例
以下は、async/awaitを使った基本的なサンプルコードです。
// Promiseを返す関数
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
// async/awaitを使った関数
async function example() {
console.log('開始');
await delay(1000); // 1秒待機
console.log('1秒後');
await delay(1000); // さらに1秒待機
console.log('2秒後');
console.log('完了');
}
example();
実行結果
エラーハンドリング
try-catch文を使ったエラーハンドリング
async/awaitでは、try-catch文を使ってエラーハンドリングを行います。さらに、finallyブロックを使用することで、成功・失敗に関わらず必ず実行される処理を記述できます。
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
// エラーハンドリングを行う
console.error('データの取得に失敗しました:', error);
} finally {
// 成功・失敗に関わらず実行される処理
console.log('データ取得処理が完了しました');
}
}
awaitを使ってはいけない場面
便利なawaitですが、使い方次第ではパフォーマンスが低下する場合があります。
ループ内でのawaitの問題
ループ内でawaitを使うと、処理が順次実行されてしまい、並列処理のメリットが失われます。
❌ 悪い例:ループ内でawait
const urls = ["a.json", "b.json", "c.json"];
async function fetchAll() {
for (const url of urls) {
const res = await fetch(url); // ←ここで毎回待ってしまう
const data = await res.json();
console.log(data);
}
}
この例では、各URLを順番に処理するため、3つのリクエストが並列実行されません。
✅ 良い例:並列で実行
const urls = ["a.json", "b.json", "c.json"];
async function fetchAll() {
const promises = urls.map(url => fetch(url)); // まとめてPromise作成
const responses = await Promise.all(promises); // 全部終わるのを待つ
const datas = await Promise.all(responses.map(r => r.json()));
console.log(datas);
}
この例では、すべてのリクエストが並列実行され、効率的です。
注意点
- ループ内のawait: 処理が順次実行され、パフォーマンスが低下
- Promise.all(): 複数の非同期処理を並列実行
- 適切な使い分け: 順次実行が必要な場合のみループ内でawaitを使用
実践演習
演習①:基本的なasync/await
以下の要件を満たすasync関数を作成してください。
- 1秒後に「Hello」を表示
- さらに1秒後に「World」を表示
- 最後に「完了」を表示
// ヒント:delay関数を使用
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
// ここにasync関数を書いてください
演習②:エラーハンドリング
以下の要件を満たすasync関数を作成してください。
- ランダムで成功または失敗する処理を実行
- 成功時は「処理が成功しました」を表示
- 失敗時は「処理が失敗しました」を表示
// ランダムで成功または失敗する関数
function randomProcess() {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve('成功');
} else {
reject('失敗');
}
}, 1000);
});
}
// ここにasync関数を書いてください
まとめ
async/awaitは、Promiseをより読みやすく書くための強力な構文です。以下のポイントを理解しましょう。
- async関数: 非同期処理を含む関数を宣言
- awaitキーワード: Promiseの完了を待機
- try-catch文: エラーハンドリング
- 可読性の向上: 同期的なコードのように書ける
- ループ内のawait: 並列処理を阻害するため注意が必要
- Promise.all(): 複数の非同期処理を効率的に並列実行
async/awaitを理解することで、非同期処理をより効率的に扱えるようになります。これでNode.jsの基本的な非同期処理の学習は完了です。