Node.js 异步编程完全指南

发布于2026-03-22 23:36 阅读12次 
# Node.js 异步编程完全指南
## 前言
Node.js 是 JavaScript 运行时,采用非阻塞 I/O 模型,天生适合处理高并发场景。异步编程是 Node.js 的核心,也是开发者必须掌握的重要技能。本文将带你从回调函数开始,逐步深入到 Promise 和 async/await,全面掌握 Node.js 的异步编程模式。
## 同步 vs 异步
首先理解同步和异步的区别:
**同步代码:**
```javascript
const fs = require("fs");
const data = fs.readFileSync("/etc/passwd");
console.log(data);
console.log("后续代码");
```
**异步代码:**
```javascript
const fs = require("fs");
fs.readFile("/etc/passwd", (err, data) => {
if (err) throw err;
console.log(data);
});
console.log("后续代码");
```
异步模式下,文件读取和后续代码会同时执行,这就是非阻塞 I/O 的威力。
## 回调函数模式
回调是 Node.js 最基础的异步方式。
```javascript
const fs = require("fs");
fs.readFile("input.txt", "utf8", (err, data) => {
if (err) {
console.error("读取失败:", err);
return;
}
console.log("内容:", data);
});
fs.writeFile("output.txt", "Hello Node.js!", (err) => {
if (err) throw err;
console.log("写入成功");
});
```
### 回调地狱(Callback Hell)
多层嵌套的回调会导致代码难以维护:
```javascript
fs.readFile("a.txt", (err, data) => {
if (err) throw err;
fs.readFile("b.txt", (err, data) => {
if (err) throw err;
fs.readFile("c.txt", (err, data) => {
if (err) throw err;
console.log(data);
});
});
});
```
## Promise 模式
Promise 代表一个异步操作的最终结果。
```javascript
const fs = require("fs").promises;
fs.readFile("input.txt", "utf8")
.then(data => {
console.log("读取成功:", data);
return fs.writeFile("output.txt", data.toUpperCase());
})
.then(() => console.log("写入成功"))
.catch(err => console.error("错误:", err));
```
### 链式调用
Promise 支持链式调用,告别回调地狱:
```javascript
fetchUser(id)
.then(user => fetchUserPosts(user.id))
.then(posts => fetchPostComments(posts[0].id))
.then(comments => console.log(comments))
.catch(err => console.error(err));
```
### Promise.all 并行执行
多个异步操作并行执行:
```javascript
async function readAllFiles() {
const [a, b, c] = await Promise.all([
fs.readFile("a.txt", "utf8"),
fs.readFile("b.txt", "utf8"),
fs.readFile("c.txt", "utf8")
]);
return { a, b, c };
}
```
## async/await 语法
async/await 是 Promise 的语法糖,让异步代码看起来像同步代码。
```javascript
const fs = require("fs").promises;
async function processFile() {
try {
const data = await fs.readFile("input.txt", "utf8");
const upperData = data.toUpperCase();
await fs.writeFile("output.txt", upperData);
console.log("处理完成");
} catch (err) {
console.error("处理失败:", err);
}
}
processFile();
```
### 并行 vs 串行
**串行执行(逐个):**
```javascript
async function serial() {
const a = await fs.readFile("a.txt");
const b = await fs.readFile("b.txt");
const c = await fs.readFile("c.txt");
}
```
**并行执行(同时):**
```javascript
async function parallel() {
const [a, b, c] = await Promise.all([
fs.readFile("a.txt"),
fs.readFile("b.txt"),
fs.readFile("c.txt")
]);
}
```
### 错误处理
```javascript
async function safeOperation() {
try {
const result = await riskyOperation();
return result;
} catch (err) {
console.error("操作失败:", err.message);
throw err;
}
}
```
## 实际应用场景
### 文件操作
```javascript
const fs = require("fs").promises;
const path = require("path");
async function copyDirectory(src, dest) {
await fs.mkdir(dest, { recursive: true });
const entries = await fs.readdir(src, { withFileTypes: true });
for (const entry of entries) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);
if (entry.isDirectory()) {
await copyDirectory(srcPath, destPath);
} else {
await fs.copyFile(srcPath, destPath);
}
}
}
```
### HTTP 请求
```javascript
const axios = require("axios");
async function fetchUserData(userIds) {
const requests = userIds.map(id =>
axios.get(`https://api.example.com/users/${id}`)
);
const responses = await Promise.allSettled(requests);
return responses
.filter(r => r.status === "fulfilled")
.map(r => r.value.data);
}
```
### 数据库操作
```javascript
const { Pool } = require("pg");
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
async function getUserWithPosts(userId) {
const client = await pool.connect();
try {
await client.query("BEGIN");
const userResult = await client.query(
"SELECT * FROM users WHERE id = $1",
[userId]
);
const postsResult = await client.query(
"SELECT * FROM posts WHERE user_id = $1",
[userId]
);
await client.query("COMMIT");
return {
user: userResult.rows[0],
posts: postsResult.rows
};
} catch (err) {
await client.query("ROLLBACK");
throw err;
} finally {
client.release();
}
}
```
## 最佳实践
1. 优先使用 async/await:代码更清晰易读
2. 正确处理错误:使用 try/catch 捕获异常
3. 避免串行等待:需要多个异步操作时考虑并行
4. 使用 Promise.all:并行执行独立任务
5. 合理使用 Promise.allSettled:不阻塞其他任务
6. 注意内存泄漏:确保释放资源(如数据库连接)
## 总结
Node.js 的异步编程经历了回调 -> Promise -> async/await 的演进。async/await 已成为现代 Node.js 开发的主流方式,它让异步代码的书写和阅读都更加自然。掌握异步编程是成为 Node.js 高手的关键,希望这篇文章能帮助你在实际项目中更好地运用这些技术。