Node.js入门教程(十四):async/await

一、什么是 async/await?

在 Node.js 中,async/await 是一种处理异步操作的语法糖。

async/await 基于 Promise,但让异步代码看起来更像同步代码,极大地提高了代码的可读性和可维护性。

async 关键字用于声明一个函数是异步的,而 await 关键字用于等待一个 Promise 的解决(resolve)或拒绝(reject)。

使用 async/await 可以避免回调地狱(callback hell)并使错误处理更加直观。

二、基本语法

async 函数

任何函数都可以通过添加 async 关键字变成异步函数:

复制代码
async function myFunction() {
    return "Hello World";
}

// 等价于
function myFunction() {
    return Promise.resolve("Hello World");
}

async 函数总是返回一个 Promise。如果返回值不是 Promise,它会被自动包装成 Promise。

await 表达式

await 只能在 async 函数内部使用,它会暂停函数的执行,等待 Promise 解决,然后继续执行并返回结果:

复制代码
async function fetchData() {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
}

三、错误处理

try/catch 方式

处理 async/await 错误最常用的方法是使用 try/catch

复制代码
async function getUser() {
    try {
        const response = await fetch('https://api.example.com/user');
        const user = await response.json();
        return user;
    } catch (error) {
        console.error('Error fetching user:', error);
        throw error; // 可以选择重新抛出错误
    }
}

直接处理 Promise

你也可以直接处理返回的 Promise:

复制代码
getUser()
    .then(user => console.log(user))
    .catch(error => console.error(error));

四、实际应用示例

并行执行多个异步操作

使用 Promise.all 结合 async/await 可以并行执行多个异步操作:

复制代码
async function fetchMultipleUrls(urls) {
    try {
        const requests = urls.map(url => fetch(url));
        const responses = await Promise.all(requests);
        const data = await Promise.all(responses.map(r => r.json()));
        return data;
    } catch (error) {
        console.error('Error fetching data:', error);
        throw error;
    }
}

// 使用
const data = await fetchMultipleUrls([
    'https://api.example.com/users',
    'https://api.example.com/posts'
]);

顺序执行 vs 并行执行

顺序执行(较慢,适合有依赖关系的操作):

复制代码
async function sequentialTasks() {
    const user = await getUser();      // 等待完成
    const posts = await getPosts();    // 再执行下一个
    return { user, posts };
}

并行执行(较快,适合独立的操作):

复制代码
async function parallelTasks() {
    const userPromise = getUser();
    const postsPromise = getPosts();
    const [user, posts] = await Promise.all([userPromise, postsPromise]);
    return { user, posts };
}

数据库操作示例

复制代码
async function getUserAndPosts(userId) {
    try {
        const user = await User.findById(userId);
        const posts = await Post.find({ userId });
        return { user, posts };
    } catch (error) {
        console.error('Database error:', error);
        throw error;
    }
}

文件操作示例

复制代码
const fs = require('fs').promises;

async function readFiles() {
    try {
        const data1 = await fs.readFile('file1.txt', 'utf8');
        const data2 = await fs.readFile('file2.txt', 'utf8');
        console.log('Data:', data1, data2);
    } catch (err) {
        console.error('读取文件出错:', err);
    }
}

五、最佳实践

实践 说明
总是处理错误 不要忽略 await 可能抛出的错误,使用 try/catch 或 .catch() 处理
避免不必要的 await 如果不需要等待结果,可以直接返回 Promise
合理使用并行 多个独立的异步操作应该并行执行(使用 Promise.all
保持代码清晰 避免过深的 async/await 嵌套,必要时提取函数
注意性能影响 每个 await 都会暂停函数执行,在循环中要特别注意

六、常见问题

async/await 与 Promise 的关系

async/await 是建立在 Promise 之上的语法糖。任何 async 函数都返回 Promise,任何 await 后面都可以接 Promise。

复制代码
// async/await 和 Promise 可以混用
async function processUser() {
    const user = await getUser();
    const posts = await getPosts(user.id);
    return posts; // 等价于 return Promise.resolve(posts)
}

// 返回的异步函数本身就是 Promise
processUser().then(posts => console.log(posts));

为什么我的 async 函数返回 undefined?

这可能是因为忘记在 await 前使用 return,或者在 Promise 解决前函数就退出了。

复制代码
// 错误示例:没有 return,返回 undefined
async function getData() {
    await fetch('url'); // 没有 return
}

// 正确示例:使用 return
async function getData() {
    return await fetch('url');
}

// 或者直接返回 Promise
async function getData() {
    return fetch('url'); // 自动包装为 Promise
}

可以在顶层使用 await 吗?

ES 模块 中(文件以 .mjs 结尾或 package.json"type": "module"),可以直接在顶层使用 await。在 CommonJS 模块中,需要包裹在 async 函数中。

复制代码
// ES 模块中可以直接使用顶层 await
const data = await fetchData();
console.log(data);

// CommonJS 模块中需要包裹
(async () => {
    const data = await fetchData();
    console.log(data);
})();

七、本章小结

知识点 说明
async 声明异步函数,返回 Promise
await 等待 Promise 解决,只能在 async 函数中使用
错误处理 使用 try/catch.catch() 处理异步错误
并行执行 使用 Promise.all 同时执行多个独立的异步操作
顺序执行 使用 await 依次执行有依赖关系的异步操作
顶层 await ES 模块中支持,CommonJS 中需包裹在 async 函数中

提示 :async/await 是现代 Node.js 异步编程的推荐方式。它让异步代码看起来像同步代码,大大提高了代码的可读性和可维护性。但要记住,await 会暂停函数执行,在循环中使用时要考虑性能影响,无关的操作应该用 Promise.all 并行执行。

相关推荐
王八八。2 小时前
nvm 安装与配置完全指南:从零到多版本 Node.js 自由切换
node.js
紫禁玄科3 小时前
Shai-Hulud:npm生态的自我复制蠕虫风暴
前端·npm·node.js
谢小飞2 天前
大文件上传很难?学会这招,GB文件秒传不是梦
前端·node.js
周小董3 天前
[1367]npm 安装 canvas 报错 node-gyp ERR
前端·npm·node.js
张龙6874 天前
Node.js 内存泄漏排查实战:从内存曲线到堆快照,一条可复用的定位路径
性能优化·node.js
烂蜻蜓4 天前
Node.js入门教程(七):工作机制
node.js
凌云拓界4 天前
NodeVerdict | .ndv 二进制格式:为 WASM 解码器设计的紧凑布局
安全·架构·开源·node.js·编辑器·软件工程·wasm
凌云拓界4 天前
NodeVerdict | 性能门禁:把追踪数据变成 CI 规则
ci/cd·信息可视化·开源·node.js·bug·数据可视化·安全架构
烂蜻蜓5 天前
Node.js入门教程(五):NVM 管理多版本 Node.js
node.js