非同期プログラミング
I/O 待ちの間に他の処理を進め、システムのスループットを向上させるプログラミング手法
プログラミングパフォーマンス
非同期プログラミングとは
非同期プログラミングは、I/O 待ち (DB クエリ、API 呼び出し、ファイル読み込み) の間に他の処理を進め、スループットを向上させる手法である。同期処理が「待つ」のに対し、非同期処理は「待たずに次へ進む」。
同期 vs 非同期
同期:
[DB クエリ 100ms] → [待ち...] → [API 呼び出し 200ms] → [待ち...] → 合計 300ms
非同期 (Promise.all):
[DB クエリ 100ms ]
[API 呼び出し 200ms ] → 合計 200ms (並行実行)
async/await
// ❌ 直列実行: 300ms
const user = await getUser(id); // 100ms
const orders = await getOrders(id); // 200ms
// ✅ 並行実行: 200ms
const [user, orders] = await Promise.all([
getUser(id),
getOrders(id),
]);
コールバック → Promise → async/await の進化
// コールバック地獄 (2010年代前半)
getUser(id, (err, user) => {
getOrders(user.id, (err, orders) => {
getProducts(orders[0].id, (err, products) => {
// ネストが深くなる...
});
});
});
// Promise チェーン (ES2015)
getUser(id)
.then(user => getOrders(user.id))
.then(orders => getProducts(orders[0].id));
// async/await (ES2017) - 同期的に読める
const user = await getUser(id);
const orders = await getOrders(user.id);
const products = await getProducts(orders[0].id);
エラーハンドリング
// try-catch で同期的にエラーを捕捉
try {
const user = await getUser(id);
} catch (error) {
console.error('Failed to get user:', error);
}
// Promise.allSettled: 一部が失敗しても全結果を取得
const results = await Promise.allSettled([
getUser('1'),
getUser('invalid'),
]);
// [{ status: 'fulfilled', value: user }, { status: 'rejected', reason: error }]
Lambda での非同期パターン
| パターン | 用途 |
|---|---|
| 同期 (API Gateway → Lambda) | リクエスト/レスポンス |
| 非同期 (SQS → Lambda) | バックグラウンド処理 |
| イベント駆動 (DynamoDB Streams → Lambda) | データ変更の処理 |
よくある間違い
awaitを忘れて Promise オブジェクトをそのまま使う- ループ内で
awaitして直列実行になる - エラーハンドリングを忘れて Unhandled Promise Rejection
全体像を把握するには関連書籍も有用。