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 并行执行。

相关推荐
阿黎梨梨14 小时前
AI也有记忆?LangChain Memory 管理指南
langchain·node.js·llm
小小龙学IT16 小时前
libuv 开源异步 I/O 事件循环库深度解析:Node.js 的心脏,C++ 高性能网络程序的引擎
c++·开源·node.js
用户672465366052 天前
Node 守护进程日志转发踩坑记:stdio 管道、UTF-8 截断,和一个字符串按值传参的故事
node.js
coderCN2 天前
Nodejs express+knex(ORM框架)
前端·node.js
coderCN2 天前
Nodejs mysql2+express+yaml
node.js
烬羽2 天前
我把"时间胶囊"部署上腾讯云宝塔,跨域从根源消失了
nginx·node.js·全栈
东风破_2 天前
danci 2:创建的单词书到底存在哪里?从 Supabase 一路理解 ORM、Drizzle 和 RLS
数据库·后端·node.js
东风破_2 天前
danci 项目(三):从 JSON 数据到 AI Coding,真实项目里的数据清洗、Prompt 和工程规范
前端·后端·node.js
东风破_2 天前
danci 项目(一):从需求到架构,一个单词学习系统为什么会这样设计
前端·后端·node.js
__zRainy__2 天前
Node系列 · Express:常用的中间件
中间件·node.js·express