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 提供了上层的语法糖,让异步代码更易读。对于文章中错误的地方或者有任何问题,欢迎在评论区留言讨论!

相关推荐
李白你好25 分钟前
Burp Suite插件用于自动检测Web应用程序中的未授权访问漏洞
前端
刘一说1 小时前
Vue 组件不必要的重新渲染问题解析:为什么子组件总在“无故”刷新?
前端·javascript·vue.js
徐同保2 小时前
React useRef 完全指南:在异步回调中访问最新的 props/state引言
前端·javascript·react.js
刘一说3 小时前
Vue 导航守卫未生效问题解析:为什么路由守卫不执行或逻辑失效?
前端·javascript·vue.js
一周七喜h3 小时前
在Vue3和TypeScripts中使用pinia
前端·javascript·vue.js
weixin_395448914 小时前
main.c_cursor_0202
前端·网络·算法
东东5164 小时前
基于vue的电商购物网站vue +ssm
java·前端·javascript·vue.js·毕业设计·毕设
MediaTea4 小时前
<span class=“js_title_inner“>Python:实例对象</span>
开发语言·前端·javascript·python·ecmascript
梦梦代码精5 小时前
开源、免费、可商用:BuildingAI一站式体验报告
开发语言·前端·数据结构·人工智能·后端·开源·知识图谱
0思必得05 小时前
[Web自动化] Selenium执行JavaScript语句
前端·javascript·爬虫·python·selenium·自动化