async/await: 同期風の非同期コード

Promise を同期コードのように書く。ES2017の革命。

async
非同期関数を宣言。Promiseを返す。
await
Promiseの完了を待つ。async内でのみ使用可。
Top-level await
ES2022でモジュールトップレベルで使用可能。

async/await

ビデオの一時停止 (Pause Button)

`await` はビデオプレーヤーの「一時停止ボタン」です。非同期処理(データのダウンロードなど)が終わるまで、その場所で映像(実行)をピタッと止めます。処理が終わると、自動的に再生ボタンが押され、次の行から続きが流れます。

なぜasync/awaitが必要か?

async/awaitは「Promiseを同期コードのように書ける構文」です。Promiseチェーン(`.then().then().then()`)は、コードが読みにくくなり、エラーハンドリングも複雑になります。async/awaitを使うと、同期的なコードのように書けるため、コードが読みやすく、エラーハンドリングも簡単になります。

いつ使うか?

async/awaitは、以下のような場面で使います: 1. **APIリクエスト**: 複数のAPIを順番に呼び出すとき。 2. **ファイル操作**: ファイルの読み込みや書き込みを順番に実行したいとき。 3. **データベース操作**: データベースへのクエリを順番に実行したいとき。 4. **エラーハンドリング**: try/catchブロックでエラーをキャッチしたいとき。

実践テクニック

並列実行を活用する

独立した非同期処理がある場合、`Promise.all()` を使って並列実行を活用してください。これにより、処理時間を大幅に短縮できます。例:複数のAPIを同時にリクエストして、すべてのレスポンスを待つ。ただし、順番が重要な場合は、順次実行(awaitを連続)を使ってください。

エラーハンドリング

async/awaitを使うと、try/catchブロックでエラーをキャッチできます。エラーが発生した場合、適切なエラーメッセージを表示し、ユーザーに通知することが重要です。また、`.catch()` メソッドを使って、Promiseチェーンでエラーハンドリングすることも可能です。

トップレベルawait

ES2022以降、モジュールのトップレベルでawaitを使えるようになりました。これにより、非同期処理を初期化する際、IIFE(即時実行関数式)を使う必要がなくなりました。ただし、トップレベルawaitは慎重に使ってください。モジュールの初期化がブロックされる可能性があるためです。

async/awaitとPromiseの使い分け

async/awaitは「同期的なコードのように書ける」ため、通常はasync/awaitを使います。ただし、`Promise.all()`、`Promise.race()`、`Promise.allSettled()` などのPromiseメソッドは、並列実行やエラーハンドリングに便利です。また、既存のPromiseチェーンをasync/awaitに書き換える場合、`try/catch` ブロックを使ってエラーハンドリングを行ってください。

async/awaitのデバッグ

async/awaitは非同期処理であるため、デバッグが難しい場合があります。Chrome DevToolsの「Sources」タブでブレークポイントを設定し、非同期処理の状態を確認してください。また、`console.log()` を使って、非同期処理の状態を出力することも有効です。

async/awaitのパフォーマンス

async/await自体は、Promiseと同じパフォーマンスを持ちます。ただし、不必要なawaitを使うと、処理が遅くなる可能性があります。例:`await Promise.resolve()` のような不必要なawaitは避けてください。また、並列実行を活用することで、処理時間を短縮できます。

Basic async/await
// async/await 基礎
async function fetchData() {
const response = await fetch('/api/data');
const data = await response.json();
return data;
}
// エラーハンドリング
async function safeFetch() {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.error('Error:', error);
}
}
Bad
// ❌ Bad: then チェーン
fetch('/api/users')
.then(res => res.json())
.then(users => fetch(`/api/posts/${users[0].id}`))
.then(res => res.json())
.then(posts => console.log(posts));
Good
// ✅ Good: async/await
async function getPosts() {
const res = await fetch('/api/users');
const users = await res.json();
const postsRes = await fetch(`/api/posts/${users[0].id}`);
return postsRes.json();
}

パターン

Common Patterns
// 並列実行
async function parallel() {
const [users, products] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/products').then(r => r.json())
]);
}
// 順次実行
async function sequential() {
const user = await getUser();
const profile = await getProfile(user.id);
const settings = await getSettings(user.id);
}
// トップレベル await (ES2022)
const config = await fetch('/config.json').then(r => r.json());
Tip: 独立した処理は Promise.all で並列化。順番が重要なときだけ順次。

合格ライン

async/await でエラーハンドリングできる
Promise.all と await の使い分けができる

演習課題

課題1: async/await
async/await で API を呼び出してください。
課題2: Promise.all
Promise.all で並列処理を実装してください。

参考文献

この記事は以下の公的ガイドライン/標準に基づいています。