JavaScript 中 async/await 的工作原理

发布于:2025-06-25 ⋅ 阅读:(21) ⋅ 点赞:(0)

1. 基本概念

async/await 是 JavaScript 中处理异步操作的语法糖,它建立在 Promise 之上,让异步代码看起来更像同步代码。

  • async 函数总是返回一个 Promise
  • await 关键字只能在 async 函数内部使用
  • await 会暂停当前 async 函数的执行,等待 Promise 解决

1.1 实现机制

  1. async 函数会自动将返回值封装为 Promise 对象
  2. await 会暂停当前 async 函数的执行,直至 Promise 状态变为 resolved
  3. Promise 被成功解析后,await 表达式会返回其解析值
  4. Promise 被拒绝,await 会抛出拒绝原因,可通过 try/catch 进行异常捕获

2. async/await 的工作原理

2.1 Generator 函数的基础

async/await 的实现原理与 Generator 函数密切相关。实际上,async/await 是对 Generator 的封装,让异步操作的写法更加优雅。

// Generator 函数写法
// 模拟异步请求函数
function fetchData1() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve({ id: 1, name: 'John' });
        }, 1000);
    });
}

function fetchData2(data) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve({
                ...data,
                age: 30,
                email: 'john@example.com'
            });
        }, 1000);
    });
}

// Generator 函数写法
function* gen() {
    console.log('开始获取用户基本信息...');
    const data1 = yield fetchData1();
    console.log('获取到的基本信息:', data1);
    
    console.log('开始获取用户详细信息...');
    const data2 = yield fetchData2(data1);
    console.log('获取到的详细信息:', data2);
    return data2;
}

// 手动执行 Generator
function runGenerator(generator) {
    const iter = generator();
    
    function run(data) {
        const result = iter.next(data);
        
        if (result.done) {
            return result.value;
        }
        
        return result.value.then(data => run(data));
    }
    
    return run();
}

// 使用 Generator
console.log('使用 Generator 方式获取数据:');
runGenerator(gen).then(finalData => {
    console.log('Generator 最终结果:', finalData);
});

// async/await 写法
async function fetchData() {
    console.log('开始获取用户基本信息...');
    const data1 = await fetchData1();
    console.log('获取到的基本信息:', data1);
    
    console.log('开始获取用户详细信息...');
    const data2 = await fetchData2(data1);
    console.log('获取到的详细信息:', data2);
    return data2;
}

// 使用 async/await
console.log('使用 async/await 方式获取数据:');
fetchData().then(finalData => {
    console.log('async/await 最终结果:', finalData);
});

2.2 内部转换机制

当我们使用 async/await 时,JavaScript 引擎会将其转换为 Promise 链。例如:

// 原始的 async/await 代码
async function example() {
    const result1 = await asyncOperation1();
    const result2 = await asyncOperation2(result1);
    console.log(result2);
    return result2;
}

// 转换后的 Promise 链(理解原理用)
function example() {
    return new Promise((resolve, reject) => {
        // 开始第一个异步操作
        asyncOperation1()
            .then(result1 => {
                // 使用第一个操作的结果开始第二个异步操作
                return asyncOperation2(result1);
            })
            .then(result2 => {
                // 打印结果
                console.log(result2);
                // 返回最终结果
                resolve(result2);
            })
            .catch(error => {
                // 处理过程中的任何错误
                reject(error);
            });
    });
}

3. 实际应用示例

3.1 基本使用示例

// 模拟异步操作
function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function fetchUserData(userId) {
    try {
        console.log('开始获取用户数据...');
        
        // 模拟API请求
        await delay(1000);
        const user = { id: userId, name: 'John Doe' };
        
        // 模拟获取用户订单
        await delay(500);
        const orders = [{ id: 1, product: 'Book' }];
        
        return { user, orders };
    } catch (error) {
        console.error('获取数据失败:', error);
        throw error;
    }
}

// 使用方式
fetchUserData(1)
    .then(data => console.log('获取的数据:', data))
    .catch(error => console.error('错误:', error));

3.2 并行执行示例

async function fetchMultipleData() {
    try {
        // 并行执行多个异步操作
        const [users, products, orders] = await Promise.all([
            fetchUsers(),
            fetchProducts(),
            fetchOrders()
        ]);
        
        return { users, products, orders };
    } catch (error) {
        console.error('获取数据失败:', error);
        throw error;
    }
}

4. 错误处理机制

async/await 提供了更直观的错误处理方式,可以使用 try/catch 语句:

async function handleErrors() {
    try {
        const data = await riskyOperation();
        return data;
    } catch (error) {
        console.error('操作失败:', error);
        // 可以选择重试、返回默认值或抛出自定义错误
        throw new CustomError('操作失败,请稍后重试');
    } finally {
        // 清理工作
        await cleanup();
    }
}

5. 常见陷阱和最佳实践

5.1 循环中的 await

// 错误示例 - 串行执行
async function processItems(items) {
    for (const item of items) {
        await processItem(item); // 每次循环都要等待
    }
}

// 正确示例 - 并行执行
async function processItems(items) {
    const promises = items.map(item => processItem(item));
    await Promise.all(promises);
}

5.2 错误传播

async function outerFunction() {
    try {
        await innerFunction();
    } catch (error) {
        // 处理所有内部 async 函数抛出的错误
        console.error('内部函数错误:', error);
    }
}

async function innerFunction() {
    throw new Error('Something went wrong');
}

6. 总结

  1. async/await 是基于 Promise 的语法糖,使异步代码更易读和维护
  2. 它的实现原理基于 Generator 函数和自动执行器
  3. 提供了更直观的错误处理机制
  4. 在实际开发中要注意避免常见陷阱,合理使用并行执行
  5. 性能优化需要考虑并行操作和超时处理

网站公告

今日签到

点亮在社区的每一天
去签到