Promise与async/await

本专栏聚焦Promise的核心原理与高级应用,包含: ✓ Promise A+规范深度解读 ✓ 手写实现与源码分析 ✓ 异步编程设计模式 ✓ 性能调优与错误处理
适合有JavaScript基础,希望深入异步编程的开发者。我们将用最少的篇幅,讲透最核心的知识。

引言

在之前的文章中,我们学习了Promise的基础和原理实现。从中我们不难发现,即使是链式调用,处理复杂的异步流程仍然有些繁琐,我们来看下面的例子:

javascript 复制代码
// Promise链式调用
fetchUser(userId)
    .then(user => fetchOrders(user.id))
    .then(orders => processOrders(orders))
    .then(result => displayResult(result))
    .catch(error => handleError(error))
    .finally(() => cleanup());

在这种情况下,async/await语法应运而生,它让我们可以用同步的方式写异步代码:

javascript 复制代码
// async/await版本
async function displayUserOrders(userId) {
    try {
        const user = await fetchUser(userId);
        const orders = await fetchOrders(user.id);
        const result = await processOrders(orders);
        displayResult(result);
    } catch (error) {
        handleError(error);
    } finally {
        cleanup();
    }
}

async:声明异步函数

基本语法

async 关键字用于声明一个异步函数:

javascript 复制代码
// async函数声明
async function getUser() {
    return { name: '张三', age: 25 };
}

// async函数表达式
const getData = async function() {
    return '数据';
};

// async箭头函数
const fetchData = async () => {
    return await fetch('/api/data');
};

// async方法
class UserService {
    async getUser(id) {
        // 异步操作
    }
}

函数返回值

async 函数总是返回一个 Promise

javascript 复制代码
async function basicReturn() {
    return 42; // 等价于 Promise.resolve(42)
}

async function promiseReturn() {
    return Promise.resolve('hello'); // 返回的Promise
}

async function throwError() {
    throw new Error('失败'); // 等价于 Promise.reject(error)
}

await:等待Promise解决

基本使用

await 只能在 async 函数内部使用,它会暂停 async 函数的执行,等待Promise解决:

javascript 复制代码
async function example() {
    console.log('开始执行');
    
    // await会暂停执行,直到Promise解决
    const result = await fetchData();
    
    console.log('数据获取完成:', result);
    return result;
}

// 等价于
function examplePromise() {
    console.log('开始执行');
    
    return fetchData().then(result => {
        console.log('数据获取完成:', result);
        return result;
    });
}

错误处理:try-catch的回归

同步风格的错误处理

javascript 复制代码
// 传统Promise错误处理(分散)
fetchUser()
    .then(user => {
        return fetchOrders(user.id)
            .catch(orderError => {
                console.error('获取订单失败:', orderError);
                return [];
            });
    })
    .catch(userError => {
        console.error('获取用户失败:', userError);
    });

// async/await错误处理(集中)
async function getUserOrders() {
    try {
        const user = await fetchUser();
        try {
            const orders = await fetchOrders(user.id);
            return orders;
        } catch (orderError) {
            console.error('获取订单失败:', orderError);
            return []; // 返回默认值
        }
    } catch (userError) {
        console.error('获取用户失败:', userError);
        throw userError; // 重新抛出
    }
}

// 更简洁的版本
async function getUserOrdersBetter() {
    try {
        const user = await fetchUser();
        const orders = await fetchOrders(user.id);
        return orders;
    } catch (error) {
        console.error('操作失败:', error);
        // 根据错误类型处理
        if (error.message.includes('用户')) {
            throw new Error('用户相关操作失败');
        }
        return []; // 默认值
    }
}

多await操作的错误处理

javascript 复制代码
async function multipleOperations() {
    // 方法1:分别处理每个await(粒度细)
    let user;
    try {
        user = await fetchUser();
    } catch (error) {
        console.error('获取用户失败');
        return null;
    }
    
    let orders;
    try {
        orders = await fetchOrders(user.id);
    } catch (error) {
        console.error('获取订单失败,但用户数据有效:', user);
        orders = [];
    }
    
    // 方法2:统一处理(粒度粗)
    try {
        const user = await fetchUser();
        const orders = await fetchOrders(user.id);
        const products = await fetchProducts(orders);
        return { user, orders, products };
    } catch (error) {
        console.error('某个步骤失败:', error);
        // 无法区分哪个步骤失败
    }
    
    // 方法3:组合使用(推荐)
    try {
        const user = await fetchUser().catch(() => {
            throw new Error('用户获取失败,终止流程');
        });
        
        const orders = await fetchOrders(user.id).catch(() => {
            console.warn('订单获取失败,使用空数组');
            return [];
        });
        
        // 即使orders为空,也继续执行
        const analysis = await analyzeData(user, orders);
        return analysis;
    } catch (error) {
        console.error('致命错误:', error);
        return null;
    }
}

停止和恢复执行

async/await 中真正起作用的是 awaitasync 关键字只是一个标识符。我们可以看下面一个例子:

javascript 复制代码
async function test() {
    console.log(await Promise.resolve(1));
}
async function test2() {
    console.log(await '2');
}

async function test3() {
    console.log(3);
}
test();
test2();
test3();

上述代码的输出结果是:3 2 1 。因为JS运行时,在碰到 await 关键字时,会记录在哪里执行暂停。等待 await 后边的值可以用了,JS运行时会向消息队列中推送一个任务,这个任务会恢复函数执行。

结语

Promiseasync/await 是JS异步编程的黄金组合:Promise 提供了底层的异步抽象和组合能力,async/await 提供了上层的语法糖,让异步代码更易读。对于文章中错误的地方或者有任何问题,欢迎在评论区留言讨论!

相关推荐
LYFlied2 小时前
前端路由核心原理深入剖析
前端
用户19017684478652 小时前
vue3规范化示例
前端
用户19017684478652 小时前
Git分支管理与代码合并实践:保持特性分支与主分支同步
前端
没有鸡汤吃不下饭2 小时前
前端打包出一个项目(文件夹),怎么本地快速启一个服务运行
前端·javascript
liusheng2 小时前
Capacitor + React 的 iOS 侧滑返回手势
前端·ios
CUYG2 小时前
v-model封装组件(定义 model 属性)
前端·vue.js
子洋2 小时前
基于远程开发的大型前端项目实践
运维·前端·后端
用户35020158847482 小时前
基于react-routet v7 的配置式 + 约定式路由系统 第一步:引入react-routerv7
前端
用户35020158847482 小时前
基于react-routet v7 的配置式 + 约定式路由系统 第二步:一个简单的约定式路由系统
前端