JavaScript

非同期処理

JavaScript

Promise、async/await、fetch

Promise

Promise の作成・連鎖・エラー処理

promises.js javascript
// 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-await.js javascript
// 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 リクエストの送受信

fetch.js javascript
// 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);

イベントループ

マイクロタスク・マクロタスクの実行順序

event-loop.js javascript
// 実行順序
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     → マクロタスク