非同期処理
JavaScript
Promise、async/await、fetch
Promise
Promise の作成・連鎖・エラー処理
// Promise の作成
const p = new Promise((resolve, reject) => {
setTimeout(() => resolve('成功!'), 1000);
// または: reject(new Error('失敗'));
});
// 消費
p.then(value => console.log(value)) // 成功時
.catch(err => console.error(err)) // 失敗時
.finally(() => console.log('完了')); // 常に実行
// チェーン(各 then は新しい Promise を返す)
fetch('/api/user')
.then(res => res.json())
.then(user => fetch(`/api/posts/${user.id}`))
.then(res => res.json())
.then(posts => console.log(posts))
.catch(err => console.error(err));
// 複数の並列実行
Promise.all([p1, p2, p3]) // 全て成功 or 1つでも失敗
Promise.allSettled([p1, p2, p3]) // 全て完了(成否問わず)
Promise.race([p1, p2, p3]) // 最初に完了したもの
Promise.any([p1, p2, p3]) // 最初に成功したもの
// 即時解決・拒否
Promise.resolve(value);
Promise.reject(new Error('失敗'));async / await
非同期処理を同期的に書く
// async 関数は常に Promise を返す
async function fetchUser(id) {
// await: Promise の解決を待つ
const res = await fetch(`/api/users/${id}`);
const user = await res.json();
return user;
}
// エラーハンドリング
async function safeLoad(id) {
try {
const user = await fetchUser(id);
return user;
} catch (err) {
console.error('取得失敗:', err.message);
return null;
}
}
// 並列実行(await を並べると直列になる!)
async function loadAll() {
// ❌ 直列(遅い)
const a = await fetchA();
const b = await fetchB();
// ✅ 並列(速い)
const [a, b] = await Promise.all([fetchA(), fetchB()]);
// ✅ 並列(エラーを個別に処理)
const results = await Promise.allSettled([fetchA(), fetchB()]);
results.forEach(r => {
if (r.status === 'fulfilled') console.log(r.value);
else console.error(r.reason);
});
}Fetch API
HTTP リクエストの送受信
// GET
const res = await fetch('/api/users');
const data = await res.json();
// ステータスチェック
if (!res.ok) {
throw new Error(`HTTP error: ${res.status}`);
}
// POST
const res = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Alice', age: 30 }),
});
// PUT / PATCH / DELETE
await fetch(`/api/users/${id}`, { method: 'PUT', body: JSON.stringify(data), headers });
await fetch(`/api/users/${id}`, { method: 'PATCH', body: JSON.stringify(patch), headers });
await fetch(`/api/users/${id}`, { method: 'DELETE' });
// URLSearchParams
const params = new URLSearchParams({ page: 1, limit: 10 });
const res = await fetch(`/api/users?${params}`);
// AbortController(タイムアウト)
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
const res = await fetch('/api/data', { signal: controller.signal });
clearTimeout(timeout);イベントループ
マイクロタスク・マクロタスクの実行順序
// 実行順序
console.log('1: 同期'); // 1番目
setTimeout(() => console.log('2: setTimeout'), 0); // マクロタスク
Promise.resolve().then(() => console.log('3: Promise')); // マイクロタスク
queueMicrotask(() => console.log('4: queueMicrotask')); // マイクロタスク
console.log('5: 同期');
// 出力順序:
// 1: 同期
// 5: 同期
// 3: Promise
// 4: queueMicrotask
// 2: setTimeout
// ポイント:
// コールスタック → マイクロタスクキュー → マクロタスクキュー
// Promise.then / async/await / queueMicrotask → マイクロタスク
// setTimeout / setInterval / DOM events → マクロタスク