普通视图

发现新文章,点击刷新页面。
今天 — 2026年2月19日首页

JavaScript 异步编程完全指南:从入门到精通

2026年2月19日 08:09

JavaScript 异步编程完全指南:从入门到精通


目录

第一部分:基础概念
  ├── 1. 为什么需要异步
  ├── 2. 事件循环机制
  └── 3. 任务队列

第二部分:回调函数
  ├── 4. 回调基础
  └── 5. 回调地狱

第三部分:Promise
  ├── 6. Promise 基础
  ├── 7. Promise 链式调用
  ├── 8. Promise 错误处理
  ├── 9. Promise 静态方法
  └── 10. 手写 Promise

第四部分:Async/Await
  ├── 11. 基本语法
  ├── 12. 错误处理
  └── 13. 常见模式

第五部分:高级异步模式
  ├── 14. Generator 与异步迭代
  ├── 15. 并发控制
  ├── 16. 发布/订阅与事件驱动
  └── 17. RxJS 响应式编程简介

第六部分:实战与最佳实践
  ├── 18. 真实项目场景
  ├── 19. 性能优化
  └── 20. 常见陷阱与调试

第一部分:基础概念

1. 为什么需要异步

1.1 JavaScript 是单线程语言

// JavaScript 只有一个主线程执行代码
// 如果所有操作都是同步的,耗时操作会阻塞后续代码

console.log("开始");

// 假设这是一个同步的网络请求(伪代码),需要3秒
// const data = syncFetch("https://api.example.com/data"); // 阻塞3秒!

console.log("结束"); // 必须等上面完成才能执行

1.2 同步 vs 异步的直观对比

// ============ 同步模型(阻塞)============
// 想象你在餐厅:点菜 → 等厨师做完 → 再点下一道
function syncExample() {
    const start = Date.now();
    
    // 模拟同步阻塞(千万别在实际项目中这样做!)
    function sleep(ms) {
        const end = Date.now() + ms;
        while (Date.now() < end) {} // 忙等待,阻塞线程
    }
    
    console.log("任务1: 开始");
    sleep(2000);  // 阻塞2秒
    console.log("任务1: 完成");
    
    console.log("任务2: 开始");
    sleep(1000);  // 阻塞1秒
    console.log("任务2: 完成");
    
    console.log(`总耗时: ${Date.now() - start}ms`); // ≈ 3000ms
}

// ============ 异步模型(非阻塞)============
// 想象你在餐厅:点完所有菜 → 哪道先做好就先上
function asyncExample() {
    const start = Date.now();
    
    console.log("任务1: 开始");
    setTimeout(() => {
        console.log(`任务1: 完成 (${Date.now() - start}ms)`);
    }, 2000);
    
    console.log("任务2: 开始");
    setTimeout(() => {
        console.log(`任务2: 完成 (${Date.now() - start}ms)`);
    }, 1000);
    
    console.log("两个任务都已发起");
    // 输出顺序:
    // 任务1: 开始
    // 任务2: 开始
    // 两个任务都已发起
    // 任务2: 完成 (≈1000ms)  ← 先完成的先执行
    // 任务1: 完成 (≈2000ms)
    // 总耗时 ≈ 2000ms(而非3000ms)
}

1.3 常见的异步操作

// 1. 定时器
setTimeout(() => console.log("延迟执行"), 1000);
setInterval(() => console.log("重复执行"), 1000);

// 2. 网络请求
fetch("https://api.github.com/users/octocat")
    .then(res => res.json())
    .then(data => console.log(data));

// 3. DOM 事件
document.addEventListener("click", (e) => {
    console.log("用户点击了", e.target);
});

// 4. 文件读写(Node.js)
const fs = require("fs");
fs.readFile("./data.txt", "utf8", (err, data) => {
    console.log(data);
});

// 5. 数据库操作
// db.query("SELECT * FROM users", (err, rows) => { ... });

// 6. Web Workers(浏览器多线程)
// const worker = new Worker("worker.js");
// worker.onmessage = (e) => console.log(e.data);

2. 事件循环机制(Event Loop)

这是理解 JS 异步的核心,必须彻底掌握!

2.1 执行模型全景图

┌─────────────────────────────────────────────────────┐
│                    调用栈 (Call Stack)                 │
│  ┌─────────────────────────────────────────────┐    │
│  │  当前正在执行的函数                            │    │
│  └─────────────────────────────────────────────┘    │
└───────────────────────┬─────────────────────────────┘
                        │
                        ▼ 当调用栈为空时
┌─────────────────────────────────────────────────────┐
│                  事件循环 (Event Loop)                 │
│     不断检查:调用栈空了吗?队列里有任务吗?            │
└───────┬──────────────────────────────┬──────────────┘
        │                              │
        ▼ 优先                         ▼ 其次
┌───────────────────┐    ┌─────────────────────────┐
│  微任务队列         │    │  宏任务队列                │
│  (Microtask Queue) │    │  (Macrotask Queue)       │
│                     │    │                           │
│  • Promise.then     │    │  • setTimeout/setInterval │
│  • MutationObserver │    │  • I/O 回调               │
│  • queueMicrotask   │    │  • UI 渲染                │
│  • process.nextTick │    │  • setImmediate (Node)    │
│    (Node.js)        │    │  • requestAnimationFrame  │
└───────────────────┘    └─────────────────────────┘

2.2 事件循环执行顺序

console.log("1. 同步代码 - script start");

setTimeout(() => {
    console.log("6. 宏任务 - setTimeout");
}, 0);

Promise.resolve()
    .then(() => {
        console.log("3. 微任务 - Promise 1");
    })
    .then(() => {
        console.log("5. 微任务 - Promise 2");
    });

queueMicrotask(() => {
    console.log("4. 微任务 - queueMicrotask");
});

console.log("2. 同步代码 - script end");

// 输出顺序(带编号):
// 1. 同步代码 - script start
// 2. 同步代码 - script end
// 3. 微任务 - Promise 1
// 4. 微任务 - queueMicrotask
// 5. 微任务 - Promise 2
// 6. 宏任务 - setTimeout

2.3 事件循环的详细步骤

/*
 * 事件循环算法:
 * 
 * 1. 执行全局同步代码(这本身就是一个宏任务)
 * 2. 调用栈清空后,检查微任务队列
 * 3. 依次执行所有微任务(包括执行过程中新产生的微任务)
 * 4. 微任务队列清空后,进行一次 UI 渲染(如果需要)
 * 5. 取出一个宏任务执行
 * 6. 回到步骤 2
 * 
 * 关键:每执行完一个宏任务,就要清空所有微任务
 */

// 经典面试题:详细分析执行顺序
console.log("script start");                          // 同步 → 立即执行

async function async1() {
    console.log("async1 start");                      // 同步 → 立即执行
    await async2();                                    
    // await 之后的代码相当于 promise.then 的回调
    console.log("async1 end");                        // 微任务
}

async function async2() {
    console.log("async2");                            // 同步 → 立即执行
}

setTimeout(function() {
    console.log("setTimeout");                        // 宏任务
}, 0);

async1();

new Promise(function(resolve) {
    console.log("promise1");                          // 同步 → 立即执行
    resolve();
}).then(function() {
    console.log("promise2");                          // 微任务
});

console.log("script end");                            // 同步 → 立即执行

/*
 * 执行分析:
 * 
 * === 第一轮:执行同步代码(全局宏任务)===
 * 调用栈:[global]
 * 输出:script start
 * 输出:async1 start
 * 输出:async2           (async2 函数体是同步的)
 *   → async1 中 await 后面的代码放入微任务队列
 * 输出:promise1          (Promise 构造函数是同步的)
 *   → then 回调放入微任务队列
 * 输出:script end
 * 
 * 此时微任务队列:[async1 end, promise2]
 * 此时宏任务队列:[setTimeout]
 * 
 * === 第二轮:清空微任务队列 ===
 * 输出:async1 end
 * 输出:promise2
 * 
 * === 第三轮:取一个宏任务 ===
 * 输出:setTimeout
 * 
 * 最终顺序:
 * script start → async1 start → async2 → promise1 → 
 * script end → async1 end → promise2 → setTimeout
 */

2.4 微任务中产生微任务

// 微任务中可以继续产生微任务,会在同一轮全部执行完
console.log("start");

setTimeout(() => console.log("timeout"), 0);

Promise.resolve()
    .then(() => {
        console.log("promise 1");
        // 在微任务中产生新的微任务
        Promise.resolve().then(() => {
            console.log("promise 1-1");
            Promise.resolve().then(() => {
                console.log("promise 1-1-1");
            });
        });
    })
    .then(() => {
        console.log("promise 2");
    });

console.log("end");

// 输出:start → end → promise 1 → promise 1-1 → promise 2 → promise 1-1-1 → timeout
// 注意:微任务全部执行完才会执行宏任务 setTimeout

// ⚠️ 危险:无限产生微任务会阻塞渲染
// Promise.resolve().then(function loop() {
//     Promise.resolve().then(loop); // 永远清不完微任务,页面卡死!
// });

2.5 Node.js 事件循环(与浏览器的区别)

/*
 * Node.js 事件循环有 6 个阶段:
 * 
 * ┌───────────────────────────┐
 * │         timers             │  ← setTimeout, setInterval
 * ├───────────────────────────┤
 * │     pending callbacks      │  ← 系统级回调(如 TCP 错误)
 * ├───────────────────────────┤
 * │       idle, prepare        │  ← 内部使用
 * ├───────────────────────────┤
 * │          poll              │  ← I/O 回调,在此阶段可能阻塞
 * ├───────────────────────────┤
 * │         check              │  ← setImmediate
 * ├───────────────────────────┤
 * │     close callbacks        │  ← socket.on('close')
 * └───────────────────────────┘
 * 
 * 每个阶段之间都会执行 process.nextTick 和 Promise 微任务
 * process.nextTick 优先级高于 Promise.then
 */

// Node.js 特有的优先级演示
process.nextTick(() => console.log("1. nextTick"));
Promise.resolve().then(() => console.log("2. promise"));
setTimeout(() => console.log("3. setTimeout"), 0);
setImmediate(() => console.log("4. setImmediate"));

// Node.js 输出:
// 1. nextTick      (最高优先级微任务)
// 2. promise       (普通微任务)
// 3. setTimeout    (timers 阶段)
// 4. setImmediate  (check 阶段)

3. 调用栈深入理解

// 调用栈是 LIFO(后进先出)结构
function multiply(a, b) {
    return a * b;           // 4. multiply 执行完毕,弹出栈
}

function square(n) {
    return multiply(n, n);  // 3. 调用 multiply,入栈
}                           // 5. square 执行完毕,弹出栈

function printSquare(n) {
    const result = square(n);  // 2. 调用 square,入栈
    console.log(result);       // 6. 调用 console.log
}

printSquare(4);  // 1. printSquare 入栈

/*
 * 调用栈变化过程:
 * 
 * Step 1: [printSquare]
 * Step 2: [printSquare, square]
 * Step 3: [printSquare, square, multiply]
 * Step 4: [printSquare, square]          ← multiply 返回
 * Step 5: [printSquare]                  ← square 返回
 * Step 6: [printSquare, console.log]
 * Step 7: [printSquare]                  ← console.log 返回
 * Step 8: []                             ← printSquare 返回
 *                                           调用栈空 → 事件循环检查队列
 */

// 栈溢出演示
function infiniteRecursion() {
    return infiniteRecursion(); // 无限递归
}
// infiniteRecursion(); 
// RangeError: Maximum call stack size exceeded

第二部分:回调函数

4. 回调函数基础

4.1 什么是回调

// 回调函数:作为参数传递给另一个函数,在适当时机被调用的函数

// === 同步回调 ===
const numbers = [1, 2, 3, 4, 5];

// forEach 的回调是同步执行的
numbers.forEach(function(num) {
    console.log(num); // 立即执行
});
console.log("forEach 之后"); // 在所有回调之后

// map 也是同步回调
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]


// === 异步回调 ===
console.log("请求开始");

// setTimeout 的回调是异步执行的
setTimeout(function callback() {
    console.log("1秒后执行"); // 至少1秒后
}, 1000);

console.log("请求已发起"); // 先于回调执行

4.2 Node.js 错误优先回调(Error-First Callback)

const fs = require('fs');

// Node.js 约定:回调的第一个参数是 error
fs.readFile('./config.json', 'utf8', function(err, data) {
    if (err) {
        // 错误处理
        if (err.code === 'ENOENT') {
            console.error('文件不存在');
        } else {
            console.error('读取失败:', err.message);
        }
        return; // 提前返回,不执行后续逻辑
    }
    
    // 成功处理
    const config = JSON.parse(data);
    console.log('配置:', config);
});

// 自己实现错误优先回调风格
function fetchUserData(userId, callback) {
    setTimeout(() => {
        if (!userId) {
            callback(new Error('userId is required'));
            return;
        }
        
        // 模拟数据库查询
        const user = {
            id: userId,
            name: 'Alice',
            email: 'alice@example.com'
        };
        
        callback(null, user); // 第一个参数为 null 表示没有错误
    }, 1000);
}

// 使用
fetchUserData(1, function(err, user) {
    if (err) {
        console.error('获取用户失败:', err.message);
        return;
    }
    console.log('用户信息:', user);
});

4.3 实际应用:事件监听器

// DOM 事件回调
const button = document.getElementById('myButton');

// 点击事件
button.addEventListener('click', function(event) {
    console.log('按钮被点击', event.target);
});

// 可以添加多个回调
button.addEventListener('click', handleClick);
button.addEventListener('mouseenter', handleHover);
button.addEventListener('mouseleave', handleLeave);

function handleClick(e) {
    console.log('处理点击');
}

function handleHover(e) {
    e.target.style.backgroundColor = '#eee';
}

function handleLeave(e) {
    e.target.style.backgroundColor = '';
}

// 移除事件监听(必须传入同一个函数引用)
button.removeEventListener('click', handleClick);

// ⚠️ 常见错误:匿名函数无法移除
// button.addEventListener('click', () => {}); // 无法移除这个监听器

5. 回调地狱(Callback Hell)

5.1 问题演示

// 需求:获取用户信息 → 获取用户订单 → 获取订单详情 → 获取物流信息

function getUserInfo(userId, callback) {
    setTimeout(() => callback(null, { id: userId, name: 'Alice' }), 300);
}

function getOrders(userId, callback) {
    setTimeout(() => callback(null, [{ orderId: 101 }, { orderId: 102 }]), 300);
}

function getOrderDetail(orderId, callback) {
    setTimeout(() => callback(null, { orderId, product: 'iPhone', trackingId: 'TK001' }), 300);
}

function getShippingInfo(trackingId, callback) {
    setTimeout(() => callback(null, { trackingId, status: '运输中', location: '上海' }), 300);
}

// 😱 回调地狱 - 金字塔形代码
getUserInfo(1, function(err, user) {
    if (err) {
        console.error('获取用户失败', err);
        return;
    }
    console.log('用户:', user.name);
    
    getOrders(user.id, function(err, orders) {
        if (err) {
            console.error('获取订单失败', err);
            return;
        }
        console.log('订单数:', orders.length);
        
        getOrderDetail(orders[0].orderId, function(err, detail) {
            if (err) {
                console.error('获取详情失败', err);
                return;
            }
            console.log('商品:', detail.product);
            
            getShippingInfo(detail.trackingId, function(err, shipping) {
                if (err) {
                    console.error('获取物流失败', err);
                    return;
                }
                console.log('物流状态:', shipping.status);
                console.log('当前位置:', shipping.location);
                
                // 如果还有更多层嵌套...
                // 代码会越来越难以维护
            });
        });
    });
});

5.2 回调地狱的问题

/*
 * 回调地狱的三大问题:
 * 
 * 1. 可读性差(Readability)
 *    - 代码向右缩进,形成"金字塔"
 *    - 逻辑流程难以追踪
 * 
 * 2. 错误处理困难(Error Handling)
 *    - 每一层都需要单独处理错误
 *    - 无法统一 catch
 *    - 容易遗漏错误处理
 * 
 * 3. 控制反转(Inversion of Control)
 *    - 把回调交给第三方库,你无法控制:
 *      - 回调是否会被调用
 *      - 回调会被调用几次
 *      - 回调是同步还是异步调用
 *      - 回调的参数是否正确
 */

// 控制反转的危险示例
function riskyThirdPartyLib(callback) {
    // 你无法控制第三方库如何调用你的回调
    callback(); // 调用了一次
    callback(); // 又调用了一次!  ← 可能导致重复计费等严重问题
    
    // 或者根本不调用
    // 或者同步调用(不在下一个 tick)
}

5.3 改善回调地狱的方法(不用 Promise)

// 方法1:命名函数 + 扁平化
function handleUser(err, user) {
    if (err) return console.error('获取用户失败', err);
    console.log('用户:', user.name);
    getOrders(user.id, handleOrders);
}

function handleOrders(err, orders) {
    if (err) return console.error('获取订单失败', err);
    console.log('订单数:', orders.length);
    getOrderDetail(orders[0].orderId, handleDetail);
}

function handleDetail(err, detail) {
    if (err) return console.error('获取详情失败', err);
    console.log('商品:', detail.product);
    getShippingInfo(detail.trackingId, handleShipping);
}

function handleShipping(err, shipping) {
    if (err) return console.error('获取物流失败', err);
    console.log('物流状态:', shipping.status);
}

// 启动链条
getUserInfo(1, handleUser);
// 代码变平了,但函数间的关系不够直观


// 方法2:使用工具库(如 async.js)
const async = require('async');

async.waterfall([
    function(cb) {
        getUserInfo(1, cb);
    },
    function(user, cb) {
        console.log('用户:', user.name);
        getOrders(user.id, cb);
    },
    function(orders, cb) {
        console.log('订单数:', orders.length);
        getOrderDetail(orders[0].orderId, cb);
    },
    function(detail, cb) {
        console.log('商品:', detail.product);
        getShippingInfo(detail.trackingId, cb);
    }
], function(err, shipping) {
    if (err) {
        console.error('流程出错:', err);
        return;
    }
    console.log('物流状态:', shipping.status);
});

第三部分:Promise

6. Promise 基础

6.1 什么是 Promise

/*
 * Promise 是一个代表异步操作最终结果的对象
 * 
 * 三种状态:
 * ┌─────────┐    resolve(value)    ┌───────────┐
 * │ pending  │ ──────────────────→ │ fulfilled  │
 * │ (等待中)  │                     │ (已成功)    │
 * └─────────┘                     └───────────┘
 *      │
 *      │  reject(reason)          ┌───────────┐
 *      └────────────────────────→ │ rejected   │
 *                                  │ (已失败)    │
 *                                  └───────────┘
 * 
 * 重要特性:
 * 1. 状态一旦改变就不可逆(pending → fulfilled 或 pending → rejected)
 * 2. 状态改变后,任何时候都可以获取结果
 */

// 创建 Promise
const promise = new Promise(function(resolve, reject) {
    // 这个函数叫做 executor(执行器),立即同步执行
    console.log("executor 执行了"); // 同步执行!
    
    // 异步操作
    setTimeout(() => {
        const success = true;
        
        if (success) {
            resolve("操作成功的数据"); // 将 promise 变为 fulfilled
        } else {
            reject(new Error("操作失败的原因")); // 将 promise 变为 rejected
        }
    }, 1000);
});

console.log("Promise 创建后"); // 在 executor 之后,在异步回调之前

// 消费 Promise
promise.then(
    function onFulfilled(value) {
        console.log("成功:", value);
    },
    function onRejected(reason) {
        console.log("失败:", reason.message);
    }
);

6.2 将回调转换为 Promise

// 改造之前回调风格的函数
function getUserInfo(userId) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (!userId) {
                reject(new Error('userId is required'));
                return;
            }
            resolve({ id: userId, name: 'Alice' });
        }, 300);
    });
}

function getOrders(userId) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve([{ orderId: 101 }, { orderId: 102 }]);
        }, 300);
    });
}

function getOrderDetail(orderId) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({ orderId, product: 'iPhone', trackingId: 'TK001' });
        }, 300);
    });
}

function getShippingInfo(trackingId) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve({ trackingId, status: '运输中', location: '上海' });
        }, 300);
    });
}

// Node.js 提供的通用转换工具
const { promisify } = require('util');
const fs = require('fs');

// 将回调风格的 fs.readFile 转为 Promise 风格
const readFile = promisify(fs.readFile);
readFile('./config.json', 'utf8').then(data => console.log(data));

// 手写 promisify
function myPromisify(fn) {
    return function(...args) {
        return new Promise((resolve, reject) => {
            fn(...args, (err, result) => {
                if (err) reject(err);
                else resolve(result);
            });
        });
    };
}

6.3 Promise 基本使用

// .then() 处理成功
// .catch() 处理失败
// .finally() 无论成功失败都执行

function fetchData(url) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (url.includes("error")) {
                reject(new Error(`请求失败: ${url}`));
            } else {
                resolve({ data: `来自 ${url} 的数据`, status: 200 });
            }
        }, 500);
    });
}

fetchData("https://api.example.com/data")
    .then(result => {
        console.log("成功:", result.data);
    })
    .catch(error => {
        console.error("失败:", error.message);
    })
    .finally(() => {
        console.log("请求完成(无论成败)");
        // 常用于:隐藏 loading、释放资源等
    });

7. Promise 链式调用

7.1 链式调用原理

/*
 * .then() 返回一个新的 Promise,这是链式调用的关键
 * 
 * 返回值规则:
 * 1. return 普通值 → 新 Promise 以该值 resolve
 * 2. return Promise → 新 Promise 跟随该 Promise 的状态
 * 3. throw 错误   → 新 Promise 以该错误 reject
 * 4. 不 return    → 新 Promise 以 undefined resolve
 */

// 基本链式调用
Promise.resolve(1)
    .then(value => {
        console.log(value); // 1
        return value + 1;   // return 普通值
    })
    .then(value => {
        console.log(value); // 2
        return Promise.resolve(value + 1); // return Promise
    })
    .then(value => {
        console.log(value); // 3
        // 不 return
    })
    .then(value => {
        console.log(value); // undefined
        throw new Error("出错了"); // throw 错误
    })
    .catch(err => {
        console.error(err.message); // "出错了"
        return "recovered"; // catch 也可以 return,链继续
    })
    .then(value => {
        console.log(value); // "recovered"
    });

7.2 用链式调用解决回调地狱

// 之前的回调地狱,用 Promise 链改写
getUserInfo(1)
    .then(user => {
        console.log('用户:', user.name);
        return getOrders(user.id);
    })
    .then(orders => {
        console.log('订单数:', orders.length);
        return getOrderDetail(orders[0].orderId);
    })
    .then(detail => {
        console.log('商品:', detail.product);
        return getShippingInfo(detail.trackingId);
    })
    .then(shipping => {
        console.log('物流状态:', shipping.status);
        console.log('当前位置:', shipping.location);
    })
    .catch(err => {
        // 统一错误处理!任何一步失败都会到这里
        console.error('流程出错:', err.message);
    });

// 代码扁平化、错误统一处理、流程清晰

7.3 链式调用中传递数据

// 问题:后面的 .then 需要用到前面多个步骤的数据

// 方案1:闭包(简单但变量多了会乱)
let savedUser;
getUserInfo(1)
    .then(user => {
        savedUser = user; // 保存到外部变量
        return getOrders(user.id);
    })
    .then(orders => {
        console.log(savedUser.name, '有', orders.length, '个订单');
    });

// 方案2:逐层传递对象(推荐)
getUserInfo(1)
    .then(user => {
        return getOrders(user.id).then(orders => ({
            user,
            orders
        }));
    })
    .then(({ user, orders }) => {
        console.log(user.name, '有', orders.length, '个订单');
        return getOrderDetail(orders[0].orderId).then(detail => ({
            user,
            orders,
            detail
        }));
    })
    .then(({ user, orders, detail }) => {
        console.log(`${user.name} 购买了 ${detail.product}`);
    });

// 方案3:async/await(最佳方案,后面会讲)
async function getFullInfo() {
    const user = await getUserInfo(1);
    const orders = await getOrders(user.id);
    const detail = await getOrderDetail(orders[0].orderId);
    const shipping = await getShippingInfo(detail.trackingId);
    
    // 所有变量都在同一作用域!
    console.log(`${user.name} 购买了 ${detail.product}${shipping.status}`);
}

8. Promise 错误处理

8.1 错误捕获机制

// .catch() 相当于 .then(undefined, onRejected)

// 方式1:.then 的第二个参数
promise.then(
    value => console.log(value),
    error => console.error(error)  // 只能捕获 promise 本身的错误
);

// 方式2:.catch()(推荐)
promise
    .then(value => {
        // 如果这里抛出错误...
        throw new Error("then 中的错误");
    })
    .catch(error => {
        // .catch 可以捕获前面所有 .then 中的错误
        console.error(error.message);
    });

// 区别演示
const p = Promise.reject(new Error("初始错误"));

// ❌ .then 的第二个参数无法捕获同一个 .then 的第一个参数中的错误
p.then(
    value => { throw new Error("then 中的错误"); },
    error => console.log("捕获:", error.message) // 捕获的是"初始错误"
);

// ✅ .catch 可以捕获链上任何位置的错误
p.then(value => {
    throw new Error("then 中的错误");
}).catch(error => {
    console.log("捕获:", error.message); // 可以捕获两种错误
});

8.2 错误传播

// 错误会沿着链向下传播,直到被 catch
Promise.resolve("start")
    .then(v => {
        console.log("step 1:", v);
        throw new Error("step 1 出错");
    })
    .then(v => {
        console.log("step 2:", v); // ❌ 跳过!不执行
    })
    .then(v => {
        console.log("step 3:", v); // ❌ 跳过!不执行
    })
    .catch(err => {
        console.log("捕获错误:", err.message); // "step 1 出错"
        return "error handled"; // 错误恢复
    })
    .then(v => {
        console.log("step 4:", v); // ✅ "error handled" — 继续执行
    });

8.3 多层错误处理

// 可以在链的不同位置放置 catch
fetchData("/api/users")
    .then(users => {
        return processUsers(users);
    })
    .catch(err => {
        // 处理获取/处理用户数据的错误
        console.warn("用户数据处理失败,使用缓存:", err.message);
        return getCachedUsers(); // 降级方案
    })
    .then(users => {
        return fetchData(`/api/users/${users[0].id}/orders`);
    })
    .catch(err => {
        // 处理获取订单的错误
        console.warn("订单获取失败:", err.message);
        return []; // 返回空数组作为默认值
    })
    .then(orders => {
        renderOrders(orders);
    })
    .catch(err => {
        // 最终的错误兜底
        showErrorPage(err);
    });

8.4 未处理的 Promise 拒绝

// ⚠️ 危险:没有 catch 的 rejected Promise
const unhandled = Promise.reject(new Error("无人处理的错误"));
// 浏览器控制台会警告:UnhandledPromiseRejectionWarning
// Node.js 15+ 会直接终止进程!

// 全局捕获未处理的 rejection

// 浏览器环境
window.addEventListener('unhandledrejection', event => {
    console.error('未处理的 Promise 拒绝:', event.reason);
    event.preventDefault(); // 阻止默认的控制台错误输出
    
    // 上报错误到监控系统
    reportError({
        type: 'unhandledrejection',
        message: event.reason?.message || String(event.reason),
        stack: event.reason?.stack
    });
});

// Node.js 环境
process.on('unhandledRejection', (reason, promise) => {
    console.error('未处理的 Promise 拒绝:', reason);
    // 推荐:记录日志后优雅退出
});

process.on('rejectionHandled', (promise) => {
    // 之前未处理的 rejection 后来被处理了
    console.log('延迟处理的 rejection');
});

9. Promise 静态方法

9.1 Promise.resolve() 和 Promise.reject()

// Promise.resolve() — 创建一个 fulfilled 的 Promise
const p1 = Promise.resolve(42);
p1.then(v => console.log(v)); // 42

// 传入 Promise 会直接返回
const p2 = Promise.resolve(Promise.resolve("hello"));
p2.then(v => console.log(v)); // "hello"(不会嵌套)

// 传入 thenable 对象(有 then 方法的对象)
const thenable = {
    then(resolve, reject) {
        resolve("from thenable");
    }
};
Promise.resolve(thenable).then(v => console.log(v)); // "from thenable"


// Promise.reject() — 创建一个 rejected 的 Promise
const p3 = Promise.reject(new Error("失败"));
p3.catch(err => console.log(err.message)); // "失败"

// 注意:reject 不会解包 Promise
const p4 = Promise.reject(Promise.resolve("嵌套"));
p4.catch(v => console.log(v)); // Promise {<fulfilled>: "嵌套"} ← 注意是 Promise 对象

9.2 Promise.all() — 全部成功才成功

/*
 * Promise.all(iterable)
 * - 所有 Promise 都 fulfilled → 结果数组(顺序与输入一致)
 * - 任何一个 rejected → 立即 rejected(快速失败)
 */

// 并行请求多个 API
const userPromise = fetch('/api/user').then(r => r.json());
const ordersPromise = fetch('/api/orders').then(r => r.json());
const settingsPromise = fetch('/api/settings').then(r => r.json());

Promise.all([userPromise, ordersPromise, settingsPromise])
    .then(([user, orders, settings]) => {
        // 三个请求都完成后才执行
        console.log('用户:', user);
        console.log('订单:', orders);
        console.log('设置:', settings);
        
        renderDashboard(user, orders, settings);
    })
    .catch(err => {
        // 任何一个失败就到这里
        console.error('加载仪表盘失败:', err);
    });

// 空数组
Promise.all([]).then(v => console.log(v)); // [](立即 fulfilled)

// 包含非 Promise 值
Promise.all([1, "hello", Promise.resolve(true)])
    .then(values => console.log(values)); // [1, "hello", true]

// 实际应用:批量上传文件
async function uploadFiles(files) {
    const uploadPromises = files.map(file => {
        return fetch('/api/upload', {
            method: 'POST',
            body: file
        });
    });
    
    try {
        const results = await Promise.all(uploadPromises);
        console.log('全部上传成功');
        return results;
    } catch (err) {
        console.error('有文件上传失败:', err);
        throw err;
    }
}

9.3 Promise.allSettled() — 等所有完成(不论成败)

/*
 * Promise.allSettled(iterable)  [ES2020]
 * - 等待所有 Promise 完成(settled = fulfilled 或 rejected)
 * - 永远不会 reject
 * - 结果数组中每个元素:
 *   { status: "fulfilled", value: ... }
 *   { status: "rejected", reason: ... }
 */

const promises = [
    fetch('/api/user'),
    fetch('/api/nonexistent'),  // 这个会失败
    fetch('/api/settings')
];

Promise.allSettled(promises)
    .then(results => {
        results.forEach((result, index) => {
            if (result.status === 'fulfilled') {
                console.log(`请求 ${index} 成功:`, result.value);
            } else {
                console.log(`请求 ${index} 失败:`, result.reason);
            }
        });
        
        // 过滤出成功的结果
        const successful = results
            .filter(r => r.status === 'fulfilled')
            .map(r => r.value);
        
        // 过滤出失败的结果
        const failed = results
            .filter(r => r.status === 'rejected')
            .map(r => r.reason);
        
        console.log(`${successful.length} 个成功, ${failed.length} 个失败`);
    });

// 实际场景:批量通知(不因某个失败就停止)
async function notifyUsers(userIds) {
    const notifications = userIds.map(id => sendNotification(id));
    const results = await Promise.allSettled(notifications);
    
    const report = {
        total: results.length,
        success: results.filter(r => r.status === 'fulfilled').length,
        failed: results.filter(r => r.status === 'rejected').length,
        errors: results
            .filter(r => r.status === 'rejected')
            .map(r => r.reason.message)
    };
    
    console.log('通知报告:', report);
    return report;
}

9.4 Promise.race() — 最快的那个

/*
 * Promise.race(iterable)
 * - 返回最先 settle 的 Promise 的结果(无论成功失败)
 */

// 超时控制
function fetchWithTimeout(url, timeoutMs = 5000) {
    const fetchPromise = fetch(url);
    
    const timeoutPromise = new Promise((_, reject) => {
        setTimeout(() => {
            reject(new Error(`请求超时 (${timeoutMs}ms): ${url}`));
        }, timeoutMs);
    });
    
    return Promise.race([fetchPromise, timeoutPromise]);
}

// 使用
fetchWithTimeout('https://api.example.com/data', 3000)
    .then(response => response.json())
    .then(data => console.log('数据:', data))
    .catch(err => console.error(err.message));

// 多个数据源竞速
function fetchFromFastestCDN(resource) {
    return Promise.race([
        fetch(`https://cdn1.example.com/${resource}`),
        fetch(`https://cdn2.example.com/${resource}`),
        fetch(`https://cdn3.example.com/${resource}`)
    ]);
}

// 注意:其他未完成的 Promise 不会被取消,只是结果被忽略

9.5 Promise.any() — 第一个成功的

/*
 * Promise.any(iterable)  [ES2021]
 * - 返回第一个 fulfilled 的 Promise
 * - 全部 rejected → 返回 AggregateError
 * 
 * vs Promise.race():
 * - race: 第一个 settled(无论成败)
 * - any:  第一个 fulfilled(忽略 rejected)
 */

// 从多个镜像获取资源
const mirrors = [
    fetch('https://mirror1.example.com/data.json'),
    fetch('https://mirror2.example.com/data.json'),
    fetch('https://mirror3.example.com/data.json')
];

Promise.any(mirrors)
    .then(response => {
        console.log('从最快的可用镜像获取到数据');
        return response.json();
    })
    .catch(err => {
        // AggregateError: All promises were rejected
        console.error('所有镜像都不可用');
        console.error('错误列表:', err.errors); // 所有错误的数组
    });

// 对比 race 和 any
const p1 = new Promise((_, reject) => setTimeout(() => reject('p1 fail'), 100));
const p2 = new Promise((resolve) => setTimeout(() => resolve('p2 success'), 200));

Promise.race([p1, p2]).catch(e => console.log('race:', e));  // "race: p1 fail"
Promise.any([p1, p2]).then(v => console.log('any:', v));      // "any: p2 success"

9.6 Promise.withResolvers() [ES2024]

/*
 * Promise.withResolvers() — 将 resolve/reject 提取到外部
 * 返回 { promise, resolve, reject }
 */

// 之前的写法
let externalResolve, externalReject;
const promise = new Promise((resolve, reject) => {
    externalResolve = resolve;
    externalReject = reject;
});

// ES2024 新写法
const { promise: p, resolve, reject } = Promise.withResolvers();

// 实际用途:在其他地方控制 Promise 的状态
class EventEmitter {
    #listeners = new Map();
    
    waitFor(eventName) {
        const { promise, resolve } = Promise.withResolvers();
        this.#listeners.set(eventName, resolve);
        return promise;
    }
    
    emit(eventName, data) {
        const resolve = this.#listeners.get(eventName);
        if (resolve) {
            resolve(data);
            this.#listeners.delete(eventName);
        }
    }
}

const emitter = new EventEmitter();
emitter.waitFor('data').then(data => console.log('收到:', data));
emitter.emit('data', { message: 'hello' }); // 收到: { message: 'hello' }

9.7 静态方法对比总结

/*
 * ┌──────────────────┬───────────────┬─────────────────────────────┐
 * │      方法         │    何时 resolve │       何时 reject            │
 * ├──────────────────┼───────────────┼─────────────────────────────┤
 * │ Promise.all      │ 全部 fulfilled │ 任一 rejected(快速失败)     │
 * │ Promise.allSettled│ 全部 settled  │ 永不 reject                  │
 * │ Promise.race     │ 首个 fulfilled │ 首个 rejected                │
 * │ Promise.any      │ 首个 fulfilled │ 全部 rejected(AggregateError)│
 * └──────────────────┴───────────────┴─────────────────────────────┘
 */

// 完整对比示例
const fast = new Promise(resolve => setTimeout(() => resolve('fast'), 100));
const slow = new Promise(resolve => setTimeout(() => resolve('slow'), 500));
const fail = new Promise((_, reject) => setTimeout(() => reject('fail'), 200));

// all: 等全部,一个失败就失败
Promise.all([fast, slow, fail]).catch(e => console.log('all:', e)); // "fail"

// allSettled: 等全部,告诉你每个的结果
Promise.allSettled([fast, slow, fail]).then(r => console.log('allSettled:', r));
// [{status:'fulfilled',value:'fast'}, {status:'fulfilled',value:'slow'}, {status:'rejected',reason:'fail'}]

// race: 第一个完成的(无论成败)
Promise.race([fast, slow, fail]).then(v => console.log('race:', v)); // "fast"

// any: 第一个成功的
Promise.any([fast, slow, fail]).then(v => console.log('any:', v)); // "fast"

10. 手写 Promise(面试高频)

class MyPromise {
    static PENDING = 'pending';
    static FULFILLED = 'fulfilled';
    static REJECTED = 'rejected';

    constructor(executor) {
        this.status = MyPromise.PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onFulfilledCallbacks = [];
        this.onRejectedCallbacks = [];

        const resolve = (value) => {
            // 处理 resolve 一个 Promise 的情况
            if (value instanceof MyPromise) {
                value.then(resolve, reject);
                return;
            }
            if (this.status === MyPromise.PENDING) {
                this.status = MyPromise.FULFILLED;
                this.value = value;
                this.onFulfilledCallbacks.forEach(fn => fn());
            }
        };

        const reject = (reason) => {
            if (this.status === MyPromise.PENDING) {
                this.status = MyPromise.REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        };

        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }

    then(onFulfilled, onRejected) {
        // 参数默认值:实现值穿透
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };

        const promise2 = new MyPromise((resolve, reject) => {
            const fulfilledMicrotask = () => {
                queueMicrotask(() => {
                    try {
                        const x = onFulfilled(this.value);
                        this.#resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                });
            };

            const rejectedMicrotask = () => {
                queueMicrotask(() => {
                    try {
                        const x = onRejected(this.reason);
                        this.#resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                });
            };

            if (this.status === MyPromise.FULFILLED) {
                fulfilledMicrotask();
            } else if (this.status === MyPromise.REJECTED) {
                rejectedMicrotask();
            } else {
                // pending 状态:收集回调
                this.onFulfilledCallbacks.push(fulfilledMicrotask);
                this.onRejectedCallbacks.push(rejectedMicrotask);
            }
        });

        return promise2;
    }

    // Promise Resolution Procedure (Promises/A+ 规范核心)
    #resolvePromise(promise2, x, resolve, reject) {
        // 不能返回自己(防止死循环)
        if (promise2 === x) {
            reject(new TypeError('Chaining cycle detected'));
            return;
        }

        if (x instanceof MyPromise) {
            x.then(resolve, reject);
        } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
            // 处理 thenable
            let called = false;
            try {
                const then = x.then;
                if (typeof then === 'function') {
                    then.call(x,
                        y => {
                            if (called) return;
                            called = true;
                            this.#resolvePromise(promise2, y, resolve, reject);
                        },
                        r => {
                            if (called) return;
                            called = true;
                            reject(r);
                        }
                    );
                } else {
                    resolve(x);
                }
            } catch (error) {
                if (called) return;
                called = true;
                reject(error);
            }
        } else {
            resolve(x);
        }
    }

    catch(onRejected) {
        return this.then(undefined, onRejected);
    }

    finally(callback) {
        return this.then(
            value => MyPromise.resolve(callback()).then(() => value),
            reason => MyPromise.resolve(callback()).then(() => { throw reason; })
        );
    }

    static resolve(value) {
        if (value instanceof MyPromise) return value;
        return new MyPromise(resolve => resolve(value));
    }

    static reject(reason) {
        return new MyPromise((_, reject) => reject(reason));
    }

    static all(promises) {
        return new MyPromise((resolve, reject) => {
            const results = [];
            let count = 0;
            const promiseArr = Array.from(promises);
            
            if (promiseArr.length === 0) {
                resolve([]);
                return;
            }

            promiseArr.forEach((p, index) => {
                MyPromise.resolve(p).then(
                    value => {
                        results[index] = value;
                        count++;
                        if (count === promiseArr.length) {
                            resolve(results);
                        }
                    },
                    reject
                );
            });
        });
    }

    static race(promises) {
        return new MyPromise((resolve, reject) => {
            for (const p of promises) {
                MyPromise.resolve(p).then(resolve, reject);
            }
        });
    }

    static allSettled(promises) {
        return new MyPromise((resolve) => {
            const results = [];
            let count = 0;
            const promiseArr = Array.from(promises);
            
            if (promiseArr.length === 0) {
                resolve([]);
                return;
            }

            promiseArr.forEach((p, index) => {
                MyPromise.resolve(p).then(
                    value => {
                        results[index] = { status: 'fulfilled', value };
                        if (++count === promiseArr.length) resolve(results);
                    },
                    reason => {
                        results[index] = { status: 'rejected', reason };
                        if (++count === promiseArr.length) resolve(results);
                    }
                );
            });
        });
    }

    static any(promises) {
        return new MyPromise((resolve, reject) => {
            const errors = [];
            let count = 0;
            const promiseArr = Array.from(promises);
            
            if (promiseArr.length === 0) {
                reject(new AggregateError([], 'All promises were rejected'));
                return;
            }

            promiseArr.forEach((p, index) => {
                MyPromise.resolve(p).then(resolve, reason => {
                    errors[index] = reason;
                    if (++count === promiseArr.length) {
                        reject(new AggregateError(errors, 'All promises were rejected'));
                    }
                });
            });
        });
    }
}

// 测试
const test = new MyPromise((resolve) => {
    setTimeout(() => resolve('hello'), 100);
});

test.then(v => {
    console.log(v); // 'hello'
    return v + ' world';
}).then(v => {
    console.log(v); // 'hello world'
});

第四部分:Async/Await

11. 基本语法

11.1 async 函数

// async 函数始终返回一个 Promise

// 声明方式
async function fetchUser() {
    return { name: 'Alice' }; // 自动包装为 Promise.resolve({ name: 'Alice' })
}

// 等价于
function fetchUser() {
    return Promise.resolve({ name: 'Alice' });
}

// 箭头函数
const fetchUser2 = async () => ({ name: 'Bob' });

// 类方法
class UserService {
    async getUser(id) {
        return { id, name: 'Charlie' };
    }
}

// 验证返回 Promise
const result = fetchUser();
console.log(result);              // Promise {<fulfilled>: { name: 'Alice' }}
console.log(result instanceof Promise); // true

result.then(user => console.log(user)); // { name: 'Alice' }

11.2 await 关键字

/*
 * await 做了什么:
 * 1. 暂停 async 函数的执行
 * 2. 等待 Promise settle
 * 3. 如果 fulfilled → 返回 value
 * 4. 如果 rejected → 抛出 reason
 * 5. 恢复 async 函数的执行
 * 
 * await 只能在 async 函数内使用(或在 ES 模块的顶层)
 */

async function demo() {
    console.log("开始");
    
    // await 一个 Promise
    const value = await new Promise(resolve => {
        setTimeout(() => resolve("异步结果"), 1000);
    });
    console.log("得到:", value); // 1秒后: "异步结果"
    
    // await 非 Promise 值会立即继续(自动包装为 Promise.resolve)
    const num = await 42;
    console.log("数字:", num); // 42
    
    // await 一个 rejected Promise 会抛出错误
    try {
        const fail = await Promise.reject(new Error("出错了"));
    } catch (err) {
        console.error("捕获:", err.message); // "出错了"
    }
    
    console.log("结束");
}

demo();

// 顶层 await(ES Modules 中)
// 在 .mjs 文件或 type:"module" 中可以直接使用
// const data = await fetch('/api/data').then(r => r.json());

11.3 用 async/await 改写 Promise 链

// Promise 链版本
function getFullUserInfo_promise(userId) {
    return getUserInfo(userId)
        .then(user => {
            return getOrders(user.id).then(orders => ({ user, orders }));
        })
        .then(({ user, orders }) => {
            return getOrderDetail(orders[0].orderId)
                .then(detail => ({ user, orders, detail }));
        })
        .then(({ user, orders, detail }) => {
            return getShippingInfo(detail.trackingId)
                .then(shipping => ({ user, orders, detail, shipping }));
        });
}

// async/await 版本 ✨
async function getFullUserInfo(userId) {
    const user = await getUserInfo(userId);
    const orders = await getOrders(user.id);
    const detail = await getOrderDetail(orders[0].orderId);
    const shipping = await getShippingInfo(detail.trackingId);
    
    return { user, orders, detail, shipping };
}

// 使用
getFullUserInfo(1).then(info => {
    console.log(`${info.user.name} 购买了 ${info.detail.product}`);
    console.log(`物流状态: ${info.shipping.status}`);
});

// 或者在另一个 async 函数中
async function main() {
    const info = await getFullUserInfo(1);
    console.log(info);
}
main();

12. 错误处理

12.1 try/catch

// 最直接的方式
async function fetchUserSafely(userId) {
    try {
        const response = await fetch(`/api/users/${userId}`);
        
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }
        
        const user = await response.json();
        return user;
    } catch (error) {
        if (error.name === 'TypeError') {
            console.error('网络错误:', error.message);
        } else {
            console.error('请求失败:', error.message);
        }
        return null; // 返回默认值
    } finally {
        hideLoadingSpinner();
    }
}

// 嵌套 try/catch 处理不同阶段的错误
async function processOrder(orderId) {
    let order;
    
    try {
        order = await fetchOrder(orderId);
    } catch (err) {
        console.error('获取订单失败');
        throw new Error('ORDER_FETCH_FAILED');
    }
    
    try {
        await validateOrder(order);
    } catch (err) {
        console.error('订单验证失败');
        throw new Error('ORDER_VALIDATION_FAILED');
    }
    
    try {
        const result = await submitPayment(order);
        return result;
    } catch (err) {
        console.error('支付失败');
        await rollbackOrder(order);
        throw new Error('PAYMENT_FAILED');
    }
}

12.2 优雅的错误处理模式

// 模式1:Go 风格的错误处理
async function to(promise) {
    try {
        const result = await promise;
        return [null, result];
    } catch (error) {
        return [error, null];
    }
}

// 使用
async function main() {
    const [err, user] = await to(getUserInfo(1));
    if (err) {
        console.error('获取用户失败:', err.message);
        return;
    }
    
    const [err2, orders] = await to(getOrders(user.id));
    if (err2) {
        console.error('获取订单失败:', err2.message);
        return;
    }
    
    console.log(user, orders);
}

// 模式2:包装函数添加错误处理
function withErrorHandler(fn, errorHandler) {
    return async function(...args) {
        try {
            return await fn.apply(this, args);
        } catch (error) {
            return errorHandler(error, ...args);
        }
    };
}

const safeGetUser = withErrorHandler(
    async (id) => {
        const response = await fetch(`/api/users/${id}`);
        return response.json();
    },
    (error, id) => {
        console.error(`获取用户 ${id} 失败:`, error);
        return null;
    }
);

const user = await safeGetUser(123);

// 模式3:装饰器模式(TypeScript/提案阶段)
function catchError(target, name, descriptor) {
    const original = descriptor.value;
    descriptor.value = async function(...args) {
        try {
            return await original.apply(this, args);
        } catch (error) {
            console.error(`${name} 执行出错:`, error);
            throw error;
        }
    };
    return descriptor;
}

12.3 重试模式

// 带指数退避的重试
async function retry(fn, options = {}) {
    const {
        maxRetries = 3,
        baseDelay = 1000,
        maxDelay = 10000,
        backoffFactor = 2,
        retryOn = () => true, // 判断是否应该重试
    } = options;

    let lastError;

    for (let attempt = 0; attempt <= maxRetries; attempt++) {
        try {
            return await fn(attempt);
        } catch (error) {
            lastError = error;
            
            if (attempt === maxRetries || !retryOn(error)) {
                throw error;
            }

            const delay = Math.min(
                baseDelay * Math.pow(backoffFactor, attempt),
                maxDelay
            );
            
            // 添加随机抖动,避免雷群效应
            const jitter = delay * 0.1 * Math.random();
            const totalDelay = delay + jitter;

            console.warn(
                `第 ${attempt + 1} 次失败,${totalDelay.toFixed(0)}ms 后重试:`, 
                error.message
            );
            
            await new Promise(resolve => setTimeout(resolve, totalDelay));
        }
    }

    throw lastError;
}

// 使用
async function fetchWithRetry(url) {
    return retry(
        async (attempt) => {
            console.log(`第 ${attempt + 1} 次尝试请求 ${url}`);
            const response = await fetch(url);
            if (!response.ok) throw new Error(`HTTP ${response.status}`);
            return response.json();
        },
        {
            maxRetries: 3,
            baseDelay: 1000,
            retryOn: (error) => {
                // 只对特定错误重试
                return error.message.includes('500') || 
                       error.message.includes('503') ||
                       error.name === 'TypeError'; // 网络错误
            }
        }
    );
}

const data = await fetchWithRetry('https://api.example.com/data');

13. Async/Await 常见模式

13.1 串行 vs 并行

// ❌ 串行执行(慢!)— 每个请求等上一个完成
async function serial() {
    const start = Date.now();
    
    const user = await fetchUser();      // 等 1 秒
    const orders = await fetchOrders();  // 再等 1 秒
    const products = await fetchProducts(); // 再等 1 秒
    
    console.log(`串行总耗时: ${Date.now() - start}ms`); // ≈ 3000ms
}

// ✅ 并行执行(快!)— 所有请求同时发出
async function parallel() {
    const start = Date.now();
    
    // 先发起所有请求(不 await)
    const userPromise = fetchUser();
    const ordersPromise = fetchOrders();
    const productsPromise = fetchProducts();
    
    // 再等待所有结果
    const user = await userPromise;
    const orders = await ordersPromise;
    const products = await productsPromise;
    
    console.log(`并行总耗时: ${Date.now() - start}ms`); // ≈ 1000ms
}

// ✅ 更推荐用 Promise.all
async function parallelWithAll() {
    const start = Date.now();
    
    const [user, orders, products] = await Promise.all([
        fetchUser(),
        fetchOrders(),
        fetchProducts()
    ]);
    
    console.log(`并行总耗时: ${Date.now() - start}ms`); // ≈ 1000ms
}

// 混合:部分串行,部分并行
async function mixed() {
    // 先获取用户(必须先有用户信息)
    const user = await fetchUser();
    
    // 然后并行获取用户的订单和收藏(互不依赖)
    const [orders, favorites] = await Promise.all([
        fetchOrders(user.id),
        fetchFavorites(user.id)
    ]);
    
    return { user, orders, favorites };
}

13.2 循环中的 async/await

const urls = [
    '/api/data/1',
    '/api/data/2',
    '/api/data/3'
];

// ❌ forEach 中的 await 不会等待!
async function badLoop() {
    urls.forEach(async (url) => {
        const data = await fetch(url);  // forEach 不会等这个
        console.log(data);
    });
    console.log("完成");  // 这行会在所有 fetch 之前执行!
}

// ✅ 串行:for...of
async function serialLoop() {
    const results = [];
    for (const url of urls) {
        const response = await fetch(url);
        const data = await response.json();
        results.push(data);
        console.log(`完成: ${url}`);
    }
    return results; // 按顺序串行执行
}

// ✅ 并行:Promise.all + map
async function parallelLoop() {
    const results = await Promise.all(
        urls.map(async (url) => {
            const response = await fetch(url);
            return response.json();
        })
    );
    return results; // 并行执行,结果顺序与 urls 一致
}

// ✅ 控制并发数的并行(后面会详细讲)
async function limitedParallelLoop() {
    const limit = 2; // 最多同时2个请求
    const results = [];
    
    for (let i = 0; i < urls.length; i += limit) {
        const batch = urls.slice(i, i + limit);
        const batchResults = await Promise.all(
            batch.map(url => fetch(url).then(r => r.json()))
        );
        results.push(...batchResults);
    }
    
    return results;
}

// ✅ for await...of(异步迭代器)
async function* fetchAll(urls) {
    for (const url of urls) {
        const response = await fetch(url);
        yield await response.json();
    }
}

async function asyncIteratorLoop() {
    for await (const data of fetchAll(urls)) {
        console.log(data);
    }
}

13.3 条件异步

// 根据条件决定是否执行异步操作
async function getUser(id, options = {}) {
    const { useCache = true } = options;
    
    // 有缓存就直接返回(同步路径)
    if (useCache) {
        const cached = cache.get(`user:${id}`);
        if (cached) return cached;
    }
    
    // 无缓存则请求(异步路径)
    const response = await fetch(`/api/users/${id}`);
    const user = await response.json();
    
    cache.set(`user:${id}`, user);
    return user;
}

// 竞态条件处理
let currentRequestId = 0;

async function search(query) {
    const requestId = ++currentRequestId;
    
    const results = await fetchSearchResults(query);
    
    // 如果在等待期间又发起了新请求,丢弃当前结果
    if (requestId !== currentRequestId) {
        console.log('过时的结果,已丢弃');
        return;
    }
    
    displayResults(results);
}

// 更好的方式:使用 AbortController
let currentController = null;

async function searchWithAbort(query) {
    // 取消之前的请求
    if (currentController) {
        currentController.abort();
    }
    
    currentController = new AbortController();
    
    try {
        const response = await fetch(`/api/search?q=${query}`, {
            signal: currentController.signal
        });
        const results = await response.json();
        displayResults(results);
    } catch (err) {
        if (err.name === 'AbortError') {
            console.log('请求已取消');
        } else {
            throw err;
        }
    }
}

13.4 async/await 与类

class DataService {
    #baseUrl;
    #cache = new Map();
    
    constructor(baseUrl) {
        this.#baseUrl = baseUrl;
    }
    
    // 异步方法
    async get(endpoint) {
        const url = `${this.#baseUrl}${endpoint}`;
        
        if (this.#cache.has(url)) {
            return this.#cache.get(url);
        }
        
        const response = await fetch(url);
        
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}`);
        }
        
        const data = await response.json();
        this.#cache.set(url, data);
        return data;
    }
    
    async post(endpoint, body) {
        const response = await fetch(`${this.#baseUrl}${endpoint}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(body)
        });
        return response.json();
    }
    
    // 静态异步方法
    static async create(baseUrl) {
        const service = new DataService(baseUrl);
        // 可以在工厂方法中做异步初始化
        await service.get('/health'); // 检查服务是否可用
        return service;
    }
}

// 使用(注意:constructor 不能是 async 的)
async function main() {
    const api = await DataService.create('https://api.example.com');
    const users = await api.get('/users');
    console.log(users);
}

第五部分:高级异步模式

14. Generator 与异步迭代

14.1 Generator 基础

// Generator 函数:可以暂停和恢复的函数
function* numberGenerator() {
    console.log("开始");
    yield 1;       // 暂停,返回 1
    console.log("继续");
    yield 2;       // 暂停,返回 2
    console.log("再继续");
    return 3;      // 结束
}

const gen = numberGenerator(); // 不会立即执行!

console.log(gen.next()); // "开始"  → { value: 1, done: false }
console.log(gen.next()); // "继续"  → { value: 2, done: false }
console.log(gen.next()); // "再继续" → { value: 3, done: true }
console.log(gen.next()); //          → { value: undefined, done: true }

// yield 可以接收外部传入的值
function* conversation() {
    const name = yield "你叫什么名字?";
    const age = yield `${name},你多大了?`;
    return `${name} 今年 ${age} 岁`;
}

const chat = conversation();
console.log(chat.next());          // { value: "你叫什么名字?", done: false }
console.log(chat.next("Alice"));   // { value: "Alice,你多大了?", done: false }
console.log(chat.next(25));        // { value: "Alice 今年 25 岁", done: true }

14.2 Generator 实现异步流程控制

// Generator + Promise = async/await 的前身

function* fetchUserFlow() {
    try {
        const user = yield getUserInfo(1);        // yield 一个 Promise
        console.log('用户:', user.name);
        
        const orders = yield getOrders(user.id);  // yield 另一个 Promise
        console.log('订单数:', orders.length);
        
        return { user, orders };
    } catch (err) {
        console.error('出错:', err.message);
    }
}

// 自动执行器(co 库的简化版)
function run(generatorFn) {
    return new Promise((resolve, reject) => {
        const gen = generatorFn();
        
        function step(nextFn) {
            let result;
            try {
                result = nextFn();
            } catch (err) {
                return reject(err);
            }
            
            if (result.done) {
                return resolve(result.value);
            }
            
            // 假设 yield 的都是 Promise
            Promise.resolve(result.value).then(
                value => step(() => gen.next(value)),    // 将结果送回 generator
                error => step(() => gen.throw(error))    // 将错误送回 generator
            );
        }
        
        step(() => gen.next());
    });
}

// 使用
run(fetchUserFlow).then(result => {
    console.log('最终结果:', result);
});

// 对比 async/await(完全等价!)
async function fetchUserAsync() {
    try {
        const user = await getUserInfo(1);
        console.log('用户:', user.name);
        
        const orders = await getOrders(user.id);
        console.log('订单数:', orders.length);
        
        return { user, orders };
    } catch (err) {
        console.error('出错:', err.message);
    }
}
// async/await 本质上就是 Generator + 自动执行器的语法糖!

14.3 异步迭代器(Async Iterator)

// Symbol.asyncIterator 和 for await...of

// 创建异步可迭代对象
const asyncIterable = {
    [Symbol.asyncIterator]() {
        let i = 0;
        return {
            async next() {
                if (i >= 3) {
                    return { value: undefined, done: true };
                }
                // 模拟异步操作
                await new Promise(resolve => setTimeout(resolve, 1000));
                return { value: i++, done: false };
            }
        };
    }
};

// 使用 for await...of
async function consume() {
    for await (const value of asyncIterable) {
        console.log(value); // 每隔1秒: 0, 1, 2
    }
}

// 异步生成器(更简洁的写法)
async function* asyncRange(start, end) {
    for (let i = start; i <= end; i++) {
        // 模拟每个值需要异步获取
        await new Promise(resolve => setTimeout(resolve, 500));
        yield i;
    }
}

async function main() {
    for await (const num of asyncRange(1, 5)) {
        console.log(num); // 每隔500ms: 1, 2, 3, 4, 5
    }
}

// 实际应用:分页获取数据
async function* fetchPages(baseUrl) {
    let page = 1;
    let hasMore = true;
    
    while (hasMore) {
        const response = await fetch(`${baseUrl}?page=${page}&limit=20`);
        const data = await response.json();
        
        yield data.items;
        
        hasMore = data.hasMore;
        page++;
    }
}

// 使用
async function getAllItems() {
    const allItems = [];
    
    for await (const items of fetchPages('/api/products')) {
        allItems.push(...items);
        console.log(`已获取 ${allItems.length} 个商品`);
    }
    
    return allItems;
}

// 实际应用:读取大文件流(Node.js)
const fs = require('fs');

async function processLargeFile(filePath) {
    const stream = fs.createReadStream(filePath, { encoding: 'utf8' });
    
    let lineCount = 0;
    for await (const chunk of stream) {
        lineCount += chunk.split('\n').length;
    }
    
    console.log(`文件共 ${lineCount} 行`);
}

15. 并发控制

15.1 并发限制器

// 实现一个通用的并发限制器
class ConcurrencyLimiter {
    #maxConcurrency;
    #running = 0;
    #queue = [];

    constructor(maxConcurrency) {
        this.#maxConcurrency = maxConcurrency;
    }

    async run(fn) {
        // 如果达到并发上限,排队等待
        if (this.#running >= this.#maxConcurrency) {
            await new Promise(resolve => this.#queue.push(resolve));
        }

        this.#running++;

        try {
            return await fn();
        } finally {
            this.#running--;
            // 释放一个排队的任务
            if (this.#queue.length > 0) {
                const next = this.#queue.shift();
                next();
            }
        }
    }

    get running() { return this.#running; }
    get pending() { return this.#queue.length; }
}

// 使用
async function downloadFiles(urls) {
    const limiter = new ConcurrencyLimiter(3); // 最多3个并行
    
    const results = await Promise.all(
        urls.map(url => 
            limiter.run(async () => {
                console.log(`开始下载: ${url} (并行: ${limiter.running})`);
                const response = await fetch(url);
                const data = await response.json();
                console.log(`完成下载: ${url}`);
                return data;
            })
        )
    );
    
    return results;
}

15.2 Promise 池

// 更精细的并发控制:Promise 池
async function promisePool(tasks, poolSize) {
    const results = [];
    const executing = new Set();

    for (const [index, task] of tasks.entries()) {
        // 创建 Promise 并开始执行
        const promise = Promise.resolve().then(() => task()).then(result => {
            results[index] = { status: 'fulfilled', value: result };
        }).catch(error => {
            results[index] = { status: 'rejected', reason: error };
        });

        executing.add(promise);
        
        // Promise 完成后从执行集合中移除
        const clean = promise.then(() => executing.delete(promise));

        // 达到池大小限制时,等待一个完成
        if (executing.size >= poolSize) {
            await Promise.race(executing);
        }
    }

    // 等待剩余的任务完成
    await Promise.all(executing);
    return results;
}

// 使用
const tasks = Array.from({ length: 20 }, (_, i) => {
    return () => new Promise(resolve => {
        const delay = Math.random() * 2000;
        setTimeout(() => {
            console.log(`任务 ${i} 完成 (耗时 ${delay.toFixed(0)}ms)`);
            resolve(`result-${i}`);
        }, delay);
    });
});

const results = await promisePool(tasks, 5);
console.log('所有结果:', results);

15.3 带进度的批量处理

async function batchProcess(items, processor, options = {}) {
    const {
        concurrency = 5,
        onProgress = () => {},
        onItemComplete = () => {},
        onItemError = () => {},
    } = options;

    const limiter = new ConcurrencyLimiter(concurrency);
    const total = items.length;
    let completed = 0;
    let failed = 0;
    const results = [];

    const promises = items.map((item, index) =>
        limiter.run(async () => {
            try {
                const result = await processor(item, index);
                results[index] = { success: true, data: result };
                onItemComplete(item, result, index);
            } catch (error) {
                results[index] = { success: false, error };
                failed++;
                onItemError(item, error, index);
            } finally {
                completed++;
                onProgress({
                    completed,
                    failed,
                    total,
                    percent: ((completed / total) * 100).toFixed(1)
                });
            }
        })
    );

    await Promise.all(promises);
    
    return {
        results,
        summary: { total, completed, failed, success: completed - failed }
    };
}

// 使用:批量上传图片
const images = ['img1.jpg', 'img2.jpg', /* ... */ 'img100.jpg'];

const report = await batchProcess(
    images,
    async (image, index) => {
        const formData = new FormData();
        formData.append('file', image);
        const response = await fetch('/api/upload', { method: 'POST', body: formData });
        if (!response.ok) throw new Error(`上传失败: ${response.status}`);
        return response.json();
    },
    {
        concurrency: 3,
        onProgress: ({ completed, total, percent }) => {
            console.log(`进度: ${completed}/${total} (${percent}%)`);
            updateProgressBar(percent);
        },
        onItemError: (image, error) => {
            console.warn(`${image} 上传失败:`, error.message);
        }
    }
);

console.log(`上传完成: ${report.summary.success} 成功, ${report.summary.failed} 失败`);

16. 发布/订阅与事件驱动

16.1 EventEmitter 实现

class AsyncEventEmitter {
    #listeners = new Map();

    on(event, listener) {
        if (!this.#listeners.has(event)) {
            this.#listeners.set(event, []);
        }
        this.#listeners.get(event).push(listener);
        return this; // 链式调用
    }

    off(event, listener) {
        const listeners = this.#listeners.get(event);
        if (listeners) {
            const index = listeners.indexOf(listener);
            if (index > -1) listeners.splice(index, 1);
        }
        return this;
    }

    once(event, listener) {
        const wrapper = async (...args) => {
            this.off(event, wrapper);
            return listener(...args);
        };
        return this.on(event, wrapper);
    }

    // 异步 emit:等待所有监听器执行完毕
    async emit(event, ...args) {
        const listeners = this.#listeners.get(event) || [];
        const results = [];
        
        for (const listener of [...listeners]) {
            results.push(await listener(...args));
        }
        
        return results;
    }

    // 并行 emit
    async emitParallel(event, ...args) {
        const listeners = this.#listeners.get(event) || [];
        return Promise.all(listeners.map(fn => fn(...args)));
    }

    // 等待某个事件触发(转为 Promise)
    waitFor(event, timeout = 0) {
        return new Promise((resolve, reject) => {
            let timer;
            
            if (timeout > 0) {
                timer = setTimeout(() => {
                    this.off(event, handler);
                    reject(new Error(`等待 "${event}" 事件超时 (${timeout}ms)`));
                }, timeout);
            }

            const handler = (data) => {
                clearTimeout(timer);
                resolve(data);
            };

            this.once(event, handler);
        });
    }
}

// 使用
const bus = new AsyncEventEmitter();

// 注册异步监听器
bus.on('order:created', async (order) => {
    console.log('发送确认邮件...');
    await sendEmail(order.userId, '订单已创建');
});

bus.on('order:created', async (order) => {
    console.log('更新库存...');
    await updateInventory(order.items);
});

// 触发事件
await bus.emit('order:created', { id: 1, userId: 'u1', items: [...] });
console.log('所有后续处理完成');

// 等待事件
const userData = await bus.waitFor('user:login', 30000);
console.log('用户登录了:', userData);

16.2 异步队列

class AsyncQueue {
    #queue = [];
    #processing = false;
    #concurrency;
    #running = 0;

    constructor(concurrency = 1) {
        this.#concurrency = concurrency;
    }

    enqueue(task) {
        return new Promise((resolve, reject) => {
            this.#queue.push({ task, resolve, reject });
            this.#process();
        });
    }

    async #process() {
        if (this.#running >= this.#concurrency || this.#queue.length === 0) {
            return;
        }

        const { task, resolve, reject } = this.#queue.shift();
        this.#running++;

        try {
            const result = await task();
            resolve(result);
        } catch (error) {
            reject(error);
        } finally {
            this.#running--;
            this.#process(); // 处理下一个
        }
    }

    get size() { return this.#queue.length; }
    get pending() { return this.#running; }

    // 等待所有任务完成
    async drain() {
        if (this.#queue.length === 0 && this.#running === 0) return;
        
        return new Promise(resolve => {
            const check = () => {
                if (this.#queue.length === 0 && this.#running === 0) {
                    resolve();
                } else {
                    setTimeout(check, 50);
                }
            };
            check();
        });
    }
}

// 使用:任务队列
const queue = new AsyncQueue(2); // 并发度 2

// 添加任务
for (let i = 0; i < 10; i++) {
    queue.enqueue(async () => {
        console.log(`开始任务 ${i}`);
        await new Promise(r => setTimeout(r, 1000));
        console.log(`完成任务 ${i}`);
        return `result-${i}`;
    }).then(result => {
        console.log(`任务结果: ${result}`);
    });
}

// 等待所有完成
await queue.drain();
console.log('所有任务已完成');

17. 可取消的异步操作

17.1 AbortController

// AbortController 是 Web API,用于取消异步操作

// 基本用法
const controller = new AbortController();
const { signal } = controller;

// 1. 取消 fetch 请求
fetch('/api/large-data', { signal })
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(err => {
        if (err.name === 'AbortError') {
            console.log('请求已取消');
        } else {
            console.error('请求失败:', err);
        }
    });

// 5秒后取消
setTimeout(() => controller.abort(), 5000);

// 2. 取消多个操作
const controller2 = new AbortController();

await Promise.all([
    fetch('/api/data1', { signal: controller2.signal }),
    fetch('/api/data2', { signal: controller2.signal }),
    fetch('/api/data3', { signal: controller2.signal }),
]);
// controller2.abort() 会同时取消所有三个请求

// 3. 监听取消信号
signal.addEventListener('abort', () => {
    console.log('收到取消信号');
    console.log('取消原因:', signal.reason);
});

// 带原因的取消
controller.abort(new Error('用户取消了操作'));

17.2 自定义可取消操作

// 让任何异步操作都可以取消
function cancellable(asyncFn) {
    const controller = new AbortController();
    
    const promise = new Promise(async (resolve, reject) => {
        // 监听取消
        controller.signal.addEventListener('abort', () => {
            reject(new DOMException('Operation cancelled', 'AbortError'));
        });
        
        try {
            const result = await asyncFn(controller.signal);
            resolve(result);
        } catch (err) {
            reject(err);
        }
    });
    
    return {
        promise,
        cancel: (reason) => controller.abort(reason)
    };
}

// 使用
const { promise, cancel } = cancellable(async (signal) => {
    const response = await fetch('/api/data', { signal });
    return response.json();
});

// 2秒后取消
setTimeout(cancel, 2000);

try {
    const data = await promise;
    console.log(data);
} catch (err) {
    if (err.name === 'AbortError') {
        console.log('已取消');
    }
}

// 可取消的延迟
function delay(ms, signal) {
    return new Promise((resolve, reject) => {
        const timer = setTimeout(resolve, ms);
        
        signal?.addEventListener('abort', () => {
            clearTimeout(timer);
            reject(new DOMException('Delay cancelled', 'AbortError'));
        });
    });
}

// 可取消的重试
async function fetchWithCancelableRetry(url, { signal, maxRetries = 3 } = {}) {
    for (let i = 0; i <= maxRetries; i++) {
        signal?.throwIfAborted(); // 检查是否已取消
        
        try {
            return await fetch(url, { signal });
        } catch (err) {
            if (err.name === 'AbortError') throw err; // 取消不重试
            if (i === maxRetries) throw err;
            
            await delay(1000 * Math.pow(2, i), signal);
        }
    }
}

18. 响应式编程简介(Observable)

// 简单的 Observable 实现
class Observable {
    constructor(subscribe) {
        this._subscribe = subscribe;
    }

    subscribe(observer) {
        // 标准化 observer
        const normalizedObserver = typeof observer === 'function'
            ? { next: observer, error: () => {}, complete: () => {} }
            : { next: () => {}, error: () => {}, complete: () => {}, ...observer };

        const subscription = this._subscribe(normalizedObserver);
        
        return {
            unsubscribe: () => {
                if (subscription?.unsubscribe) subscription.unsubscribe();
            }
        };
    }

    // 操作符
    map(fn) {
        return new Observable(observer => {
            return this.subscribe({
                next: value => observer.next(fn(value)),
                error: err => observer.error(err),
                complete: () => observer.complete()
            });
        });
    }

    filter(predicate) {
        return new Observable(observer => {
            return this.subscribe({
                next: value => predicate(value) && observer.next(value),
                error: err => observer.error(err),
                complete: () => observer.complete()
            });
        });
    }

    // 从各种来源创建 Observable
    static fromEvent(element, eventName) {
        return new Observable(observer => {
            const handler = event => observer.next(event);
            element.addEventListener(eventName, handler);
            return {
                unsubscribe: () => element.removeEventListener(eventName, handler)
            };
        });
    }

    static fromPromise(promise) {
        return new Observable(observer => {
            promise
                .then(value => {
                    observer.next(value);
                    observer.complete();
                })
                .catch(err => observer.error(err));
        });
    }

    static interval(ms) {
        return new Observable(observer => {
            let i = 0;
            const id = setInterval(() => observer.next(i++), ms);
            return { unsubscribe: () => clearInterval(id) };
        });
    }
}

// 使用示例:搜索框防抖
const searchInput = document.getElementById('search');

const subscription = Observable.fromEvent(searchInput, 'input')
    .map(e => e.target.value)
    .filter(text => text.length >= 2)
    .subscribe({
        next: async (query) => {
            const results = await fetch(`/api/search?q=${query}`).then(r => r.json());
            displayResults(results);
        }
    });

// 取消订阅
// subscription.unsubscribe();

第六部分:实战与最佳实践

19. 真实项目场景

19.1 完整的 API 客户端

class APIClient {
    #baseUrl;
    #defaultHeaders;
    #interceptors = { request: [], response: [] };
    #timeout;

    constructor(config = {}) {
        this.#baseUrl = config.baseUrl || '';
        this.#defaultHeaders = config.headers || {};
        this.#timeout = config.timeout || 30000;
    }

    // 拦截器
    addRequestInterceptor(fn) {
        this.#interceptors.request.push(fn);
        return this;
    }

    addResponseInterceptor(fn) {
        this.#interceptors.response.push(fn);
        return this;
    }

    async #request(method, endpoint, options = {}) {
        let config = {
            method,
            url: `${this.#baseUrl}${endpoint}`,
            headers: { ...this.#defaultHeaders, ...options.headers },
            body: options.body,
            params: options.params,
            timeout: options.timeout || this.#timeout,
            signal: options.signal,
        };

        // 执行请求拦截器
        for (const interceptor of this.#interceptors.request) {
            config = await interceptor(config);
        }

        // 构建 URL(处理查询参数)
        const url = new URL(config.url);
        if (config.params) {
            Object.entries(config.params).forEach(([key, value]) => {
                url.searchParams.append(key, value);
            });
        }

        // 超时控制
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), config.timeout);

        // 合并 signal
        const signal = config.signal
            ? anySignal([config.signal, controller.signal])
            : controller.signal;

        try {
            const fetchOptions = {
                method: config.method,
                headers: config.headers,
                signal,
            };

            if (config.body && method !== 'GET') {
                fetchOptions.body = JSON.stringify(config.body);
                fetchOptions.headers['Content-Type'] = 'application/json';
            }

            let response = await fetch(url.toString(), fetchOptions);

            // 执行响应拦截器
            for (const interceptor of this.#interceptors.response) {
                response = await interceptor(response);
            }

            if (!response.ok) {
                const error = new Error(`HTTP ${response.status}: ${response.statusText}`);
                error.status = response.status;
                error.response = response;
                throw error;
            }

            const contentType = response.headers.get('content-type');
            if (contentType?.includes('application/json')) {
                return await response.json();
            }
            return await response.text();

        } finally {
            clearTimeout(timeoutId);
        }
    }

    get(endpoint, options) { return this.#request('GET', endpoint, options); }
    post(endpoint, body, options) { return this.#request('POST', endpoint, { ...options, body }); }
    put(endpoint, body, options) { return this.#request('PUT', endpoint, { ...options, body }); }
    patch(endpoint, body, options) { return this.#request('PATCH', endpoint, { ...options, body }); }
    delete(endpoint, options) { return this.#request('DELETE', endpoint, options); }
}

// 使用
const api = new APIClient({
    baseUrl: 'https://api.example.com',
    headers: {
        'Accept': 'application/json',
    },
    timeout: 10000,
});

// 添加认证拦截器
api.addRequestInterceptor(async (config) => {
    const token = await getAuthToken();
    config.headers['Authorization'] = `Bearer ${token}`;
    return config;
});

// 添加日志拦截器
api.addResponseInterceptor(async (response) => {
    console.log(`${response.url}${response.status}`);
    return response;
});

// 调用
try {
    const users = await api.get('/users', { params: { page: 1, limit: 20 } });
    const newUser = await api.post('/users', { name: 'Alice', email: 'alice@example.com' });
} catch (err) {
    if (err.status === 401) {
        // 跳转登录
    }
}

19.2 缓存与去重

// 请求去重 + 缓存
class RequestCache {
    #cache = new Map();        // 结果缓存
    #pending = new Map();      // 进行中的请求(去重)
    #ttl;

    constructor(ttl = 60000) { // 默认缓存1分钟
        this.#ttl = ttl;
    }

    async get(key, fetcher) {
        // 1. 检查缓存
        const cached = this.#cache.get(key);
        if (cached && Date.now() - cached.timestamp < this.#ttl) {
            console.log(`[Cache HIT] ${key}`);
            return cached.data;
        }

        // 2. 检查是否有相同的请求正在进行(去重)
        if (this.#pending.has(key)) {
            console.log(`[Cache DEDUP] ${key}`);
            return this.#pending.get(key);
        }

        // 3. 发起新请求
        console.log(`[Cache MISS] ${key}`);
        const promise = fetcher().then(data => {
            // 成功后缓存结果
            this.#cache.set(key, { data, timestamp: Date.now() });
            this.#pending.delete(key);
            return data;
        }).catch(err => {
            this.#pending.delete(key);
            throw err;
        });

        this.#pending.set(key, promise);
        return promise;
    }

    invalidate(key) {
        this.#cache.delete(key);
    }

    clear() {
        this.#cache.clear();
    }
}

// 使用
const cache = new RequestCache(30000); // 30秒缓存

async function getUser(id) {
    return cache.get(`user:${id}`, () => 
        fetch(`/api/users/${id}`).then(r => r.json())
    );
}

// 即使同时调用多次,也只会发一个请求
const [user1, user2, user3] = await Promise.all([
    getUser(1),  // 发起请求
    getUser(1),  // 复用同一个请求(去重)
    getUser(1),  // 复用同一个请求(去重)
]);
// 后续调用使用缓存
const user4 = await getUser(1); // Cache HIT

19.3 WebSocket 封装

class ReconnectableWebSocket {
    #url;
    #ws = null;
    #options;
    #reconnectAttempts = 0;
    #listeners = new Map();
    #messageQueue = [];
    #isConnected = false;

    constructor(url, options = {}) {
        this.#url = url;
        this.#options = {
            maxReconnectAttempts: 10,
            reconnectInterval: 1000,
            maxReconnectInterval: 30000,
            ...options
        };
        this.#connect();
    }

    #connect() {
        this.#ws = new WebSocket(this.#url);

        this.#ws.onopen = () => {
            console.log('[WS] 连接成功');
            this.#isConnected = true;
            this.#reconnectAttempts = 0;
            
            // 发送队列中的消息
            while (this.#messageQueue.length > 0) {
                const msg = this.#messageQueue.shift();
                this.#ws.send(msg);
            }
            
            this.#emit('open');
        };

        this.#ws.onmessage = (event) => {
            try {
                const data = JSON.parse(event.data);
                this.#emit('message', data);
                
                // 支持按类型分发
                if (data.type) {
                    this.#emit(`message:${data.type}`, data.payload);
                }
            } catch {
                this.#emit('message', event.data);
            }
        };

        this.#ws.onclose = (event) => {
            this.#isConnected = false;
            console.log(`[WS] 连接关闭: ${event.code}`);
            this.#emit('close', event);
            
            if (event.code !== 1000) { // 非正常关闭
                this.#reconnect();
            }
        };

        this.#ws.onerror = (error) => {
            console.error('[WS] 错误:', error);
            this.#emit('error', error);
        };
    }

    #reconnect() {
        if (this.#reconnectAttempts >= this.#options.maxReconnectAttempts) {
            console.error('[WS] 达到最大重连次数');
            this.#emit('maxReconnectAttemptsReached');
            return;
        }

        const delay = Math.min(
            this.#options.reconnectInterval * Math.pow(2, this.#reconnectAttempts),
            this.#options.maxReconnectInterval
        );

        console.log(`[WS] ${delay}ms 后重连 (第 ${this.#reconnectAttempts + 1} 次)`);
        
        setTimeout(() => {
            this.#reconnectAttempts++;
            this.#connect();
        }, delay);
    }

    send(data) {
        const message = typeof data === 'string' ? data : JSON.stringify(data);
        
        if (this.#isConnected) {
            this.#ws.send(message);
        } else {
            this.#messageQueue.push(message); // 离线时先队列中
        }
    }

    // 发送请求并等待响应
    request(type, payload, timeout = 5000) {
        return new Promise((resolve, reject) => {
            const requestId = Math.random().toString(36).slice(2);
            
            const timer = setTimeout(() => {
                this.off(`message:${type}:${requestId}`, handler);
                reject(new Error(`WebSocket 请求超时: ${type}`));
            }, timeout);

            const handler = (response) => {
                clearTimeout(timer);
                resolve(response);
            };

            this.once(`message:${type}:${requestId}`, handler);
            this.send({ type, payload, requestId });
        });
    }

    on(event, handler) {
        if (!this.#listeners.has(event)) {
            this.#listeners.set(event, new Set());
        }
        this.#listeners.get(event).add(handler);
        return this;
    }

    off(event, handler) {
        this.#listeners.get(event)?.delete(handler);
        return this;
    }

    once(event, handler) {
        const wrapper = (...args) => {
            this.off(event, wrapper);
            handler(...args);
        };
        return this.on(event, wrapper);
    }

    #emit(event, ...args) {
        this.#listeners.get(event)?.forEach(handler => handler(...args));
    }

    close() {
        this.#options.maxReconnectAttempts = 0;
        this.#ws?.close(1000, 'Client closed');
    }
}

// 使用
const ws = new ReconnectableWebSocket('wss://api.example.com/ws');

ws.on('open', () => console.log('已连接'));
ws.on('message:chat', (msg) => console.log('收到消息:', msg));
ws.on('message:notification', (notif) => showNotification(notif));

ws.send({ type: 'join', payload: { room: 'general' } });

// 请求-响应模式
const userList = await ws.request('getUserList', { room: 'general' });

20. 性能优化

20.1 防抖与节流

// 防抖(Debounce):等用户停止操作后再执行
function debounce(fn, delay, options = {}) {
    const { leading = false, trailing = true } = options;
    let timer = null;
    let lastArgs = null;

    function debounced(...args) {
        lastArgs = args;
        
        const callNow = leading && !timer;
        
        clearTimeout(timer);
        
        timer = setTimeout(() => {
            timer = null;
            if (trailing && lastArgs) {
                fn(...lastArgs);
                lastArgs = null;
            }
        }, delay);

        if (callNow) {
            fn(...args);
        }
    }

    debounced.cancel = () => {
        clearTimeout(timer);
        timer = null;
        lastArgs = null;
    };

    // 返回 Promise 版本
    debounced.promise = (...args) => {
        return new Promise((resolve) => {
            debounced((...result) => resolve(fn(...result)));
        });
    };

    return debounced;
}

// 异步防抖搜索
const debouncedSearch = debounce(async (query) => {
    const results = await fetch(`/api/search?q=${query}`).then(r => r.json());
    displayResults(results);
}, 300);

searchInput.addEventListener('input', (e) => {
    debouncedSearch(e.target.value);
});


// 节流(Throttle):限制执行频率
function throttle(fn, interval) {
    let lastTime = 0;
    let timer = null;

    return function(...args) {
        const now = Date.now();
        const remaining = interval - (now - lastTime);

        if (remaining <= 0) {
            clearTimeout(timer);
            timer = null;
            lastTime = now;
            fn(...args);
        } else if (!timer) {
            timer = setTimeout(() => {
                lastTime = Date.now();
                timer = null;
                fn(...args);
            }, remaining);
        }
    };
}

// 滚动事件节流
window.addEventListener('scroll', throttle(async () => {
    if (isNearBottom()) {
        await loadMoreItems();
    }
}, 200));

20.2 懒加载与预加载

// 懒加载模式
class LazyLoader {
    #loaders = new Map();
    #cache = new Map();

    register(key, loader) {
        this.#loaders.set(key, loader);
    }

    async get(key) {
        // 已缓存
        if (this.#cache.has(key)) {
            return this.#cache.get(key);
        }

        const loader = this.#loaders.get(key);
        if (!loader) throw new Error(`Unknown resource: ${key}`);

        const value = await loader();
        this.#cache.set(key, value);
        return value;
    }

    // 预加载(后台提前加载)
    preload(...keys) {
        return Promise.allSettled(
            keys.map(key => this.get(key))
        );
    }
}

// 使用
const resources = new LazyLoader();

resources.register('heavyModule', () => import('./heavy-module.js'));
resources.register('userProfile', () => fetch('/api/profile').then(r => r.json()));
resources.register('config', () => fetch('/api/config').then(r => r.json()));

// 只有在需要时才加载
const profile = await resources.get('userProfile');

// 路由跳转前预加载下一页的资源
router.beforeEach((to) => {
    if (to.name === 'dashboard') {
        resources.preload('config', 'userProfile');
    }
});


// 图片懒加载
function lazyLoadImages() {
    const observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                const img = entry.target;
                img.src = img.dataset.src;
                img.classList.remove('lazy');
                observer.unobserve(img);
            }
        });
    }, { rootMargin: '200px' }); // 提前200px开始加载

    document.querySelectorAll('img.lazy').forEach(img => {
        observer.observe(img);
    });
}

20.3 Web Worker 异步计算

// main.js — 将耗时计算放到 Worker 线程
class WorkerPool {
    #workers = [];
    #queue = [];
    #maxWorkers;

    constructor(workerScript, maxWorkers = navigator.hardwareConcurrency || 4) {
        this.#maxWorkers = maxWorkers;
        
        for (let i = 0; i < maxWorkers; i++) {
            this.#workers.push({
                worker: new Worker(workerScript),
                busy: false
            });
        }
    }

    execute(data) {
        return new Promise((resolve, reject) => {
            const task = { data, resolve, reject };
            
            const freeWorker = this.#workers.find(w => !w.busy);
            if (freeWorker) {
                this.#runTask(freeWorker, task);
            } else {
                this.#queue.push(task);
            }
        });
    }

    #runTask(workerInfo, task) {
        workerInfo.busy = true;
        
        const handleMessage = (e) => {
            workerInfo.worker.removeEventListener('message', handleMessage);
            workerInfo.worker.removeEventListener('error', handleError);
            workerInfo.busy = false;
            
            task.resolve(e.data);
            
            // 处理队列中的下一个任务
            if (this.#queue.length > 0) {
                const nextTask = this.#queue.shift();
                this.#runTask(workerInfo, nextTask);
            }
        };
        
        const handleError = (e) => {
            workerInfo.worker.removeEventListener('message', handleMessage);
            workerInfo.worker.removeEventListener('error', handleError);
            workerInfo.busy = false;
            task.reject(e.error || new Error(e.message));
        };
        
        workerInfo.worker.addEventListener('message', handleMessage);
        workerInfo.worker.addEventListener('error', handleError);
        workerInfo.worker.postMessage(task.data);
    }

    terminate() {
        this.#workers.forEach(w => w.worker.terminate());
    }
}

// worker.js
// self.onmessage = function(e) {
//     const { type, payload } = e.data;
//     
//     switch (type) {
//         case 'heavyComputation':
//             const result = performHeavyWork(payload);
//             self.postMessage(result);
//             break;
//     }
// };

// 使用
const pool = new WorkerPool('worker.js', 4);

const results = await Promise.all([
    pool.execute({ type: 'heavyComputation', payload: data1 }),
    pool.execute({ type: 'heavyComputation', payload: data2 }),
    pool.execute({ type: 'heavyComputation', payload: data3 }),
]);

21. 常见陷阱与调试

21.1 常见陷阱

// 陷阱1:忘记 await
async function trap1() {
    const promise = fetchData(); // ❌ 忘记 await
    console.log(promise);        // Promise {<pending>},不是数据!
    
    const data = await fetchData(); // ✅
    console.log(data);              // 实际数据
}

// 陷阱2:forEach 中使用 async/await
async function trap2() {
    const ids = [1, 2, 3];
    
    // ❌ forEach 不会等待 async 回调
    ids.forEach(async (id) => {
        const data = await fetchData(id);
        console.log(data);
    });
    console.log("完成"); // 在所有 fetchData 之前执行!
    
    // ✅ 使用 for...of
    for (const id of ids) {
        const data = await fetchData(id);
        console.log(data);
    }
    console.log("完成"); // 在所有 fetchData 之后执行
    
    // ✅ 或 Promise.all + map(并行)
    await Promise.all(ids.map(async (id) => {
        const data = await fetchData(id);
        console.log(data);
    }));
    console.log("完成");
}

// 陷阱3:async 函数中的返回值
async function trap3() {
    // ❌ 在 try/catch 的 catch 中 return 但忘记前面的逻辑可能已执行
    try {
        const data = await riskyOperation();
        updateUI(data);
        return data;
    } catch (err) {
        return null; // 但 updateUI 可能已经部分执行了!
    }
}

// 陷阱4:Promise 构造函数中的异步操作
// ❌ 在 Promise 构造函数中使用 async
const badPromise = new Promise(async (resolve, reject) => {
    try {
        const data = await fetchData();
        resolve(data);
    } catch (err) {
        // 如果这里抛出错误,不会被外部 catch 到!
        reject(err);
    }
});

// ✅ 直接使用 async 函数
async function goodApproach() {
    return await fetchData();
}

// 陷阱5:竞态条件
let currentData = null;

async function trap5(query) {
    // ❌ 快速调用可能导致旧请求覆盖新请求
    const data = await search(query);
    currentData = data; // 如果之前的请求比后面的慢,会覆盖新数据
}

// ✅ 使用请求 ID 或 AbortController
let requestCounter = 0;
async function safeSearch(query) {
    const myRequestId = ++requestCounter;
    const data = await search(query);
    
    if (myRequestId === requestCounter) {
        currentData = data; // 只使用最新请求的结果
    }
}

// 陷阱6:内存泄漏
class trap6Component {
    constructor() {
        this.controller = new AbortController();
    }
    
    async loadData() {
        try {
            const data = await fetch('/api/data', {
                signal: this.controller.signal
            });
            this.render(data);
        } catch (err) {
            if (err.name !== 'AbortError') throw err;
        }
    }
    
    // ✅ 组件销毁时取消未完成的请求
    destroy() {
        this.controller.abort();
    }
}

// 陷阱7:错误吞噬
async function trap7() {
    // ❌ catch 后不重新抛出,调用者不知道出错了
    try {
        await riskyOperation();
    } catch (err) {
        console.error(err); // 只是打印,没有抛出
    }
    // 调用者以为一切正常...
    
    // ✅ 要么重新抛出,要么返回明确的错误标志
    try {
        await riskyOperation();
    } catch (err) {
        console.error(err);
        throw err; // 重新抛出让调用者知道
    }
}

21.2 调试技巧

// 1. 使用 async stack traces
// Chrome DevTools → Settings → Enable async stack traces

// 2. 给 Promise 打标签
function labeledFetch(label, url) {
    const promise = fetch(url).then(r => r.json());
    promise.label = label; // 调试用
    return promise;
}

// 3. 日志包装器
function traced(fn, name) {
    return async function(...args) {
        const id = Math.random().toString(36).slice(2, 8);
        console.log(`[${name}:${id}] 开始`, args);
        const start = performance.now();
        
        try {
            const result = await fn.apply(this, args);
            const duration = (performance.now() - start).toFixed(2);
            console.log(`[${name}:${id}] 完成 (${duration}ms)`, result);
            return result;
        } catch (err) {
            const duration = (performance.now() - start).toFixed(2);
            console.error(`[${name}:${id}] 失败 (${duration}ms)`, err);
            throw err;
        }
    };
}

const tracedFetch = traced(
    (url) => fetch(url).then(r => r.json()),
    'API'
);

await tracedFetch('/api/users');
// [API:k3m2n1] 开始 ["/api/users"]
// [API:k3m2n1] 完成 (234.56ms) [{...}, {...}]

// 4. Promise 状态检查
async function inspectPromise(promise) {
    const unique = Symbol();
    const result = await Promise.race([promise, Promise.resolve(unique)]);
    
    if (result === unique) {
        return 'pending';
    }
    return 'fulfilled';
}

// 5. 性能监测
class PerformanceTracker {
    #marks = new Map();
    
    start(label) {
        this.#marks.set(label, performance.now());
    }
    
    end(label) {
        const start = this.#marks.get(label);
        if (!start) throw new Error(`No start mark for: ${label}`);
        
        const duration = performance.now() - start;
        this.#marks.delete(label);
        
        console.log(`⏱️ ${label}: ${duration.toFixed(2)}ms`);
        return duration;
    }
    
    async measure(label, fn) {
        this.start(label);
        try {
            return await fn();
        } finally {
            this.end(label);
        }
    }
}

const perf = new PerformanceTracker();

await perf.measure('加载用户数据', async () => {
    return fetch('/api/users').then(r => r.json());
});
// ⏱️ 加载用户数据: 156.78ms

22. 完整知识图谱总结

JavaScript 异步编程知识图谱
│
├── 基础概念
│   ├── 单线程模型
│   ├── 事件循环 ★★★
│   │   ├── 调用栈 (Call Stack)
│   │   ├── 宏任务队列 (setTimeout, setInterval, I/O)
│   │   ├── 微任务队列 (Promise.then, queueMicrotask, MutationObserver)
│   │   └── 执行顺序:同步 → 微任务(全部) → 宏任务(一个) → 微任务(全部) → ...
│   └── Node.js 事件循环(6个阶段)
│
├── 回调函数
│   ├── 错误优先回调
│   ├── 回调地狱
│   └── 控制反转问题
│
├── Promise ★★★
│   ├── 三种状态:pending → fulfilled / rejected
│   ├── 链式调用(.then 返回新 Promise)
│   ├── 错误处理(.catch 错误传播)
│   ├── 静态方法
│   │   ├── Promise.all (全部成功)
│   │   ├── Promise.allSettled (全部完成)
│   │   ├── Promise.race (最快)
│   │   ├── Promise.any (第一个成功)
│   │   └── Promise.withResolvers (ES2024)
│   └── 手写 Promise(面试)
│
├── Async/Await ★★★
│   ├── async 函数返回 Promise
│   ├── await 暂停执行等待 Promise
│   ├── 错误处理(try/catch)
│   ├── 串行 vs 并行
│   └── 循环中的 await
│
├── 高级模式
│   ├── Generator + 自动执行器
│   ├── 异步迭代器 (for await...of)
│   ├── 并发控制(限流器、Promise池)
│   ├── AbortController(取消操作)
│   ├── 发布/订阅模式
│   └── Observable(响应式编程)
│
└── 实战技巧
    ├── 重试机制(指数退避)
    ├── 请求去重与缓存
    ├── 防抖与节流
    ├── 竞态条件处理
    ├── Web Worker
    └── 常见陷阱
        ├── forEach 中的 async
        ├── 忘记 await
        ├── 未处理的 rejection
        └── 内存泄漏

学习路线建议:

  1. 入门:理解同步/异步 → 事件循环 → 回调
  2. 基础:Promise 创建/消费 → 链式调用 → 错误处理
  3. 进阶:async/await → 并行/串行 → 静态方法
  4. 高级:并发控制 → 取消操作 → Generator/异步迭代
  5. 精通:手写 Promise → 架构设计 → 性能优化 → 响应式编程
❌
❌