Appearance
Node.js 异步编程
Node.js是单线程的,通过异步编程来处理高并发请求。本章详细介绍Node.js中的异步编程模式。
回调函数
回调函数是Node.js中最基本的异步处理方式。
基本回调函数
javascript
const fs = require('fs');
// 异步读取文件
fs.readFile('./data.txt', 'utf8', (err, data) => {
if (err) {
console.error('读取文件失败:', err);
return;
}
console.log('文件内容:', data);
});
console.log('这行会先执行'); // 这行会先执行
回调地狱问题
javascript
// 回调地狱示例
getData(function(a) {
getMoreData(a, function(b) {
getEvenMoreData(b, function(c) {
getEvenEvenMoreData(c, function(d) {
// 嵌套过深,难以维护
console.log(d);
});
});
});
});
错误优先回调
Node.js中的异步操作通常使用错误优先回调模式:
javascript
function asyncOperation(param, callback) {
// 模拟异步操作
setTimeout(() => {
if (param) {
callback(null, `处理成功: ${param}`); // 第一个参数为null表示无错误
} else {
callback(new Error('参数无效')); // 第一个参数为错误对象
}
}, 1000);
}
asyncOperation('test', (err, result) => {
if (err) {
console.error('错误:', err.message);
} else {
console.log('结果:', result);
}
});
Promise
Promise是解决回调地狱的一种方式,提供了更好的错误处理和链式调用。
创建Promise
javascript
function asyncOperation(param) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (param) {
resolve(`处理成功: ${param}`);
} else {
reject(new Error('参数无效'));
}
}, 1000);
});
}
// 使用Promise
asyncOperation('test')
.then(result => {
console.log('结果:', result);
return asyncOperation('chained');
})
.then(result => {
console.log('链式结果:', result);
})
.catch(err => {
console.error('错误:', err.message);
});
Promise常用方法
javascript
const fs = require('fs').promises;
const util = require('util');
// 将回调函数转换为Promise
const readFileAsync = util.promisify(fs.readFile);
// Promise.all - 并行执行所有Promise
Promise.all([
asyncOperation('task1'),
asyncOperation('task2'),
asyncOperation('task3')
])
.then(results => {
console.log('所有任务完成:', results);
})
.catch(err => {
console.error('其中一个任务失败:', err.message);
});
// Promise.race - 返回第一个完成的Promise
Promise.race([
asyncOperation('fast'),
asyncOperation('slow')
])
.then(result => {
console.log('最快完成:', result);
});
// Promise.allSettled - 等待所有Promise完成(无论成功或失败)
Promise.allSettled([
asyncOperation('success'),
Promise.reject(new Error('失败'))
])
.then(results => {
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`任务${index}成功:`, result.value);
} else {
console.log(`任务${index}失败:`, result.reason.message);
}
});
});
async/await
async/await是基于Promise的语法糖,使异步代码看起来像同步代码。
基本用法
javascript
async function asyncFunction() {
try {
const result1 = await asyncOperation('task1');
console.log('任务1结果:', result1);
const result2 = await asyncOperation('task2');
console.log('任务2结果:', result2);
return '所有任务完成';
} catch (err) {
console.error('错误:', err.message);
throw err;
}
}
// 调用async函数
asyncFunction()
.then(result => {
console.log('最终结果:', result);
})
.catch(err => {
console.error('最终错误:', err.message);
});
并行执行async/await
javascript
async function parallelExecution() {
try {
// 并行执行
const [result1, result2, result3] = await Promise.all([
asyncOperation('task1'),
asyncOperation('task2'),
asyncOperation('task3')
]);
console.log('并行结果:', result1, result2, result3);
} catch (err) {
console.error('并行执行错误:', err.message);
}
}
// 或者先启动所有异步操作,再等待结果
async function parallelExecution2() {
try {
// 启动所有异步操作
const promise1 = asyncOperation('task1');
const promise2 = asyncOperation('task2');
const promise3 = asyncOperation('task3');
// 等待所有操作完成
const result1 = await promise1;
const result2 = await promise2;
const result3 = await promise3;
console.log('并行结果:', result1, result2, result3);
} catch (err) {
console.error('并行执行错误:', err.message);
}
}
事件驱动异步编程
Node.js的事件驱动模型也是异步编程的重要组成部分。
javascript
const EventEmitter = require('events');
class AsyncProcessor extends EventEmitter {
async process(data) {
this.emit('start', data);
try {
const result = await asyncOperation(data);
this.emit('success', result);
return result;
} catch (err) {
this.emit('error', err);
throw err;
} finally {
this.emit('end');
}
}
}
const processor = new AsyncProcessor();
processor.on('start', (data) => {
console.log('开始处理:', data);
});
processor.on('success', (result) => {
console.log('处理成功:', result);
});
processor.on('error', (err) => {
console.error('处理失败:', err.message);
});
processor.on('end', () => {
console.log('处理结束');
});
// 使用
processor.process('test data');
流(Streams)中的异步操作
流是Node.js中处理数据的异步方式:
javascript
const fs = require('fs');
const { Transform } = require('stream');
// 创建转换流
const upperCaseStream = new Transform({
transform(chunk, encoding, callback) {
// 异步处理每个数据块
setImmediate(() => {
const upperChunk = chunk.toString().toUpperCase();
callback(null, upperChunk);
});
}
});
// 使用流进行异步处理
const readStream = fs.createReadStream('./input.txt');
const writeStream = fs.createWriteStream('./output.txt');
readStream
.pipe(upperCaseStream)
.pipe(writeStream)
.on('finish', () => {
console.log('流处理完成');
});
异步操作最佳实践
1. 错误处理
javascript
// 不好的做法
async function badExample() {
const result = await someAsyncOperation(); // 没有错误处理
return result;
}
// 好的做法
async function goodExample() {
try {
const result = await someAsyncOperation();
return result;
} catch (err) {
console.error('异步操作失败:', err);
throw new Error(`操作失败: ${err.message}`);
}
}
2. 避免阻塞
javascript
// 不好的做法 - 阻塞事件循环
async function badLoop() {
const items = Array.from({length: 1000000}, (_, i) => i);
for (const item of items) {
// 执行大量同步操作,会阻塞事件循环
heavySyncOperation(item);
}
}
// 好的做法 - 分批处理,给其他任务执行机会
async function goodLoop() {
const items = Array.from({length: 1000000}, (_, i) => i);
for (let i = 0; i < items.length; i++) {
heavySyncOperation(items[i]);
// 每处理1000个元素后让出控制权
if (i % 1000 === 0) {
await new Promise(resolve => setImmediate(resolve));
}
}
}
3. 合理使用并发控制
javascript
// 控制并发数量
async function controlledConcurrency(tasks, concurrency = 3) {
const results = [];
const executing = [];
for (const [index, task] of tasks.entries()) {
const promise = task().then(result => {
results[index] = result;
});
executing.push(promise);
if (executing.length >= concurrency) {
await Promise.race(executing);
executing.splice(executing.findIndex(p => p === promise), 1);
}
}
await Promise.all(executing);
return results;
}
4. 异步函数中的性能优化
javascript
// 数据库查询示例
class DatabaseService {
async getUserWithPosts(userId) {
// 并行执行独立的查询
const [user, posts] = await Promise.all([
this.getUser(userId),
this.getPostsByUserId(userId)
]);
return { user, posts };
}
async getBatchUsers(userIds) {
// 批量查询优化
const users = await Promise.all(
userIds.map(id => this.getUser(id))
);
return users;
}
}
异步编程模式对比
| 模式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 回调函数 | 简单直观 | 容易形成回调地狱 | 简单的异步操作 |
| Promise | 链式调用,错误处理统一 | 语法相对复杂 | 需要链式处理的场景 |
| async/await | 代码清晰,类似同步写法 | 需要Promise基础 | 复杂的异步逻辑 |
Node.js的异步编程是其高性能的关键,掌握这些异步编程模式对于开发高质量的Node.js应用至关重要。