JS/TS:Promise 和 async/await 完全指南

Promise 和 async/await 完全指南

1. 为什么需要 Promise 和 async/await?

传统回调函数的问题

javascript 复制代码
// 回调地狱示例
getData(function(a) {
    getMoreData(a, function(b) {
        getEvenMoreData(b, function(c) {
            getEvenEvenMoreData(c, function(d) {
                // 处理最终结果
                console.log(d);
            });
        });
    });
});

问题:

  • 代码难以阅读和维护
  • 错误处理困难
  • 难以进行流程控制

2. Promise 基础概念

什么是 Promise?

Promise 是一个代表异步操作最终完成或失败的对象。

Promise 的三种状态

javascript 复制代码
// 状态转换图
Pending(等待) → Fulfilled(成功) → Settled(已敲定)
           ↘ Rejected(失败) ↗

Promise 基本语法

javascript 复制代码
// 创建 Promise
const myPromise = new Promise((resolve, reject) => {
    // 异步操作
    setTimeout(() => {
        const success = true;
        if (success) {
            resolve("操作成功!"); // 成功时调用
        } else {
            reject("操作失败!");  // 失败时调用
        }
    }, 1000);
});

// 使用 Promise
myPromise
    .then(result => {
        console.log("成功:", result);
    })
    .catch(error => {
        console.log("失败:", error);
    });

3. Promise 实际应用示例

示例1:模拟网络请求

javascript 复制代码
// 模拟 API 请求
function fetchUserData(userId) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            if (userId > 0) {
                resolve({
                    id: userId,
                    name: `用户${userId}`,
                    email: `user${userId}@example.com`
                });
            } else {
                reject(new Error("无效的用户ID"));
            }
        }, 1000);
    });
}

// 使用 Promise
fetchUserData(1)
    .then(user => {
        console.log("获取用户信息:", user);
        return fetchUserData(2); // 链式调用
    })
    .then(user2 => {
        console.log("获取第二个用户:", user2);
    })
    .catch(error => {
        console.error("错误:", error.message);
    });

示例2:Promise.all 并行处理

javascript 复制代码
// 同时获取多个用户信息
const promises = [
    fetchUserData(1),
    fetchUserData(2),
    fetchUserData(3)
];

Promise.all(promises)
    .then(users => {
        console.log("所有用户信息:", users);
    })
    .catch(error => {
        console.error("其中一个请求失败:", error);
    });

4. async/await 语法

基本概念

async/await 是 Promise 的语法糖,让异步代码看起来像同步代码。

基本用法

javascript 复制代码
// async 函数
async function getUserInfo(userId) {
    try {
        // await 等待 Promise 完成
        const user = await fetchUserData(userId);
        console.log("用户信息:", user);
        return user;
    } catch (error) {
        console.error("获取用户失败:", error.message);
        throw error; // 重新抛出错误
    }
}

// 调用 async 函数
getUserInfo(1).then(user => {
    console.log("最终结果:", user);
});

实际应用示例

javascript 复制代码
// 顺序执行多个异步操作
async function processUsers() {
    try {
        console.log("开始获取用户...");
        
        const user1 = await fetchUserData(1);
        console.log("用户1:", user1);
        
        const user2 = await fetchUserData(2);
        console.log("用户2:", user2);
        
        return [user1, user2];
    } catch (error) {
        console.error("处理用户时出错:", error);
        throw error;
    }
}

// 并行执行多个异步操作
async function processUsersParallel() {
    try {
        console.log("并行获取用户...");
        
        // 同时发起多个请求
        const [user1, user2, user3] = await Promise.all([
            fetchUserData(1),
            fetchUserData(2),
            fetchUserData(3)
        ]);
        
        console.log("所有用户:", user1, user2, user3);
        return [user1, user2, user3];
    } catch (error) {
        console.error("并行处理失败:", error);
        throw error;
    }
}

5. 完整实战示例

javascript 复制代码
// 模拟真实的 API 调用场景
class ApiService {
    // 模拟 HTTP 请求
    static request(url, data = {}) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                // 模拟 80% 成功率
                if (Math.random() > 0.2) {
                    resolve({
                        success: true,
                        data: { ...data, id: Date.now(), timestamp: new Date() },
                        message: "请求成功"
                    });
                } else {
                    reject(new Error("网络请求失败"));
                }
            }, Math.random() * 1000 + 500); // 随机延迟 500-1500ms
        });
    }
}

// 使用 Promise 的方式
function fetchUserWithPromise(userId) {
    return ApiService.request(`/api/users/${userId}`, { userId })
        .then(response => {
            console.log(`Promise方式 - 用户${userId}获取成功`);
            return response.data;
        })
        .catch(error => {
            console.error(`Promise方式 - 获取用户${userId}失败:`, error.message);
            throw error;
        });
}

// 使用 async/await 的方式
async function fetchUserWithAsync(userId) {
    try {
        const response = await ApiService.request(`/api/users/${userId}`, { userId });
        console.log(`Async方式 - 用户${userId}获取成功`);
        return response.data;
    } catch (error) {
        console.error(`Async方式 - 获取用户${userId}失败:`, error.message);
        throw error;
    }
}

// 实际使用示例
async function main() {
    console.log("=== Promise 方式 ===");
    try {
        const user1 = await fetchUserWithPromise(1);
        const user2 = await fetchUserWithPromise(2);
        console.log("Promise结果:", user1, user2);
    } catch (error) {
        console.error("Promise流程出错:", error.message);
    }

    console.log("\n=== Async/Await 方式 ===");
    try {
        const user3 = await fetchUserWithAsync(3);
        const user4 = await fetchUserWithAsync(4);
        console.log("Async结果:", user3, user4);
    } catch (error) {
        console.error("Async流程出错:", error.message);
    }

    console.log("\n=== 并行处理 ===");
    try {
        const results = await Promise.all([
            fetchUserWithAsync(5),
            fetchUserWithAsync(6),
            fetchUserWithAsync(7)
        ]);
        console.log("并行结果:", results);
    } catch (error) {
        console.error("并行处理出错:", error.message);
    }
}

// 运行示例
main();

6. 重要注意事项

错误处理

javascript 复制代码
// ❌ 错误的错误处理
async function badExample() {
    const user = await fetchUserData(1);
    const posts = await fetchUserPosts(user.id); // 如果这里出错,下面的代码不会执行
    return { user, posts };
}

// ✅ 正确的错误处理
async function goodExample() {
    try {
        const user = await fetchUserData(1);
        const posts = await fetchUserPosts(user.id);
        return { user, posts };
    } catch (error) {
        console.error("操作失败:", error);
        // 可以选择重新抛出或返回默认值
        return { user: null, posts: [] };
    }
}

性能优化

javascript 复制代码
// ❌ 顺序执行(慢)
async function slowWay() {
    const user = await fetchUserData(1);      // 等待 1 秒
    const posts = await fetchUserPosts(1);    // 再等待 1 秒
    const comments = await fetchComments(1);  // 再等待 1 秒
    // 总共 3 秒
}

// ✅ 并行执行(快)
async function fastWay() {
    // 同时发起所有请求
    const [user, posts, comments] = await Promise.all([
        fetchUserData(1),
        fetchUserPosts(1),
        fetchComments(1)
    ]);
    // 总共 1 秒
}

避免常见陷阱

javascript 复制代码
// ❌ 不要在循环中使用 await(会变成顺序执行)
async function badLoop() {
    const results = [];
    for (let i = 1; i <= 3; i++) {
        const result = await fetchUserData(i); // 一个接一个执行
        results.push(result);
    }
    return results;
}

// ✅ 在循环中使用 Promise.all 实现并行
async function goodLoop() {
    const promises = [];
    for (let i = 1; i <= 3; i++) {
        promises.push(fetchUserData(i)); // 同时发起所有请求
    }
    const results = await Promise.all(promises);
    return results;
}

7. 总结

Promise vs async/await 选择指南

使用 Promise 的场景:

  • 简单的异步操作
  • 需要链式调用
  • 与旧代码兼容

使用 async/await 的场景:

  • 复杂的异步逻辑
  • 需要错误处理
  • 代码可读性要求高

最佳实践

  1. 总是处理错误 - 使用 .catch()try/catch
  2. 合理使用并行 - 用 Promise.all() 优化性能
  3. 避免回调地狱 - 优先使用 Promise 或 async/await
  4. 保持一致性 - 在项目中统一使用一种风格

通过掌握这些概念和技巧,你就能轻松处理 JavaScript 中的异步编程了!

相关推荐
阿虎儿7 分钟前
TypeScript 内置工具类型完全指南
前端·javascript·typescript
chxii1 小时前
6.3Element UI 的表单
javascript·vue.js·elementui
深兰科技1 小时前
深兰科技:搬迁公告,我们搬家了
javascript·人工智能·python·科技·typescript·laravel·深兰科技
lumi.1 小时前
Swiper属性全解析:快速掌握滑块视图核心配置!(2.3补充细节,详细文档在uniapp官网)
前端·javascript·css·小程序·uni-app
芝士加2 小时前
还在用html2canvas?介绍一个比它快100倍的截图神器!
前端·javascript·开源
阿虎儿2 小时前
React 引用(Ref)完全指南
前端·javascript·react.js
前端小大白2 小时前
JavaScript 循环三巨头:for vs forEach vs map 终极指南
前端·javascript·面试
晴空雨2 小时前
面试题:如何判断一个对象是否为可迭代对象?
前端·javascript·面试
阿虎儿2 小时前
React 事件类型完全指南:深入理解合成事件系统
前端·javascript·react.js
Hilaku2 小时前
前端需要掌握多少Node.js?
前端·javascript·node.js