☕ NEW! 完成新手任務即可參加抽獎!LINE 星巴克禮券等你拿,名額有限!        🎉 推廣活動:邀請好友註冊 DevLearn,累積推薦抽 LINE 星巴克禮券! 活動詳情 →        🔥 活動期間 2026/4/1 - 5/31 |已有 0 人參加       
javascript 中級

Promise 與 async/await

同步 vs 非同步

比喻: 🍳

  • 同步:煎完蛋 → 才能烤吐司 → 才能倒咖啡(一件接一件)
  • 非同步:同時煎蛋 + 烤吐司 + 泡咖啡(不用等前一件做完)

JavaScript 是單執行緒,但透過非同步機制避免「卡住」。


回呼地獄(Callback Hell)

// 舊方式:巢狀回呼
getUser(1, function(user) {
    getOrders(user.id, function(orders) {
        getOrderDetails(orders[0].id, function(details) {
            console.log(details);
            // 越來越深...這就是回呼地獄 🔥
        });
    });
});

Promise — 解決回呼地獄

// Promise 有三種狀態:
// pending(等待中)→ fulfilled(成功)或 rejected(失敗)

// 建立 Promise
function fetchUser(id) {
    return new Promise((resolve, reject) => {  // ← 建立 Promise
        setTimeout(() => {
            if (id > 0) {
                resolve({ id, name: "小明" });   // ← 成功,傳出資料
            } else {
                reject(new Error("ID 無效"));     // ← 失敗,傳出錯誤
            }
        }, 1000);
    });
}

// 使用 Promise
fetchUser(1)
    .then(user => {                            // ← 成功時執行
        console.log(user.name);
        return fetchOrders(user.id);           // ← 回傳新的 Promise
    })
    .then(orders => {                          // ← 鏈式呼叫
        console.log(orders);
    })
    .catch(error => {                          // ← 失敗時執行(任何一步失敗都會到這)
        console.error("錯誤:", error.message);
    })
    .finally(() => {                           // ← 不管成功失敗都執行
        console.log("完成");
    });

async / await — Promise 的語法糖

// async 函式永遠回傳 Promise
async function getUser(id) {
    // await 等待 Promise 完成,取得結果
    let response = await fetch(`/api/users/${id}`);  // ← 等待 HTTP 請求完成
    let user = await response.json();                 // ← 等待 JSON 解析完成
    return user;                                      // ← 自動包成 Promise
}

// 呼叫
async function main() {
    try {
        let user = await getUser(1);          // ← 等待結果
        console.log(user.name);

        let orders = await getOrders(user.id); // ← 繼續下一步
        console.log(orders);
    } catch (error) {                          // ← 錯誤處理
        console.error("出錯了:", error.message);
    }
}

main();

對比

// Promise 鏈
fetchUser(1)
    .then(user => fetchOrders(user.id))
    .then(orders => console.log(orders))
    .catch(err => console.error(err));

// async/await(更像同步程式碼,更好讀)
async function main() {
    let user = await fetchUser(1);
    let orders = await fetchOrders(user.id);
    console.log(orders);
}

並行執行

// ❌ 串行(等 A 完才跑 B)— 慢
async function slow() {
    let userA = await fetchUser(1);    // 等 1 秒
    let userB = await fetchUser(2);    // 再等 1 秒
    // 總共 2 秒
}

// ✅ 並行(A 和 B 同時跑)— 快
async function fast() {
    let [userA, userB] = await Promise.all([
        fetchUser(1),                  // 同時開始
        fetchUser(2)                   // 同時開始
    ]);
    // 總共 1 秒
}

// Promise.allSettled — 全部完成(不管成功失敗)
let results = await Promise.allSettled([
    fetchUser(1),     // 成功
    fetchUser(-1),    // 失敗
]);
// results[0] = { status: "fulfilled", value: {...} }
// results[1] = { status: "rejected", reason: Error }

// Promise.race — 最快的那個
let fastest = await Promise.race([
    fetchFromServer1(),
    fetchFromServer2()
]);
// 回傳最先完成的結果

實用模式

// 帶超時的請求
async function fetchWithTimeout(url, ms) {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), ms);

    try {
        let response = await fetch(url, { signal: controller.signal });
        return await response.json();
    } finally {
        clearTimeout(timeout);
    }
}

// 重試機制
async function fetchWithRetry(url, retries = 3) {
    for (let i = 0; i < retries; i++) {
        try {
            return await fetch(url).then(r => r.json());
        } catch (err) {
            if (i === retries - 1) throw err;
            await new Promise(r => setTimeout(r, 1000 * (i + 1)));
        }
    }
}

💡 大家的想法 · 0

載入中...
💬 即時聊天室 🟢 0 人在線
😀 😎 🤓 💻 🎮 🎸 🔥
➕ 新問題
📋 我的工單
💬 LINE 社群
🔒
需要註冊才能使用此功能
註冊帳號即可解鎖測驗、遊戲、簽到、筆記下載等所有功能,完全免費!
免費註冊