Node.js 回调函数的基本用法是通过将函数作为参数传递给异步操作,在操作完成后执行该函数。以下是详细说明:
1. 回调函数的基本概念
回调函数是 Node.js 处理异步操作的核心机制,遵循 "error-first" 约定。
// 基本格式
function callback(error, result) {
if (error) {
// 处理错误
console.error('错误:', error);
return;
}
// 处理成功结果
console.log('结果:', result);
}
2. 实际应用示例
文件读取示例
const fs = require('fs');
// 读取文件(异步操作)
fs.readFile('example.txt', 'utf8', (err, data) => {
if (err) {
console.error('读取文件出错:', err);
return;
}
console.log('文件内容:', data);
});
自定义异步函数
// 创建使用回调的自定义函数
function getUserData(userId, callback) {
setTimeout(() => {
if (userId > 0) {
callback(null, {
id: userId,
name: '张三',
email: 'zhangsan@example.com'
});
} else {
callback(new Error('用户ID无效'));
}
}, 1000);
}
// 使用自定义函数
getUserData(123, (err, user) => {
if (err) {
console.error('获取用户数据失败:', err.message);
return;
}
console.log('用户数据:', user);
});
3. 回调金字塔(回调地狱)及解决方法
回调嵌套问题
// 回调地狱示例
fs.readFile('file1.txt', 'utf8', (err, data1) => {
if (err) return console.error(err);
fs.readFile('file2.txt', 'utf8', (err, data2) => {
if (err) return console.error(err);
fs.writeFile('result.txt', data1 + data2, (err) => {
if (err) return console.error(err);
console.log('操作完成');
});
});
});
解决方案:Promise 或 Async/Await
// 使用 Promise
const fs = require('fs').promises;
async function processFiles() {
try {
const data1 = await fs.readFile('file1.txt', 'utf8');
const data2 = await fs.readFile('file2.txt', 'utf8');
await fs.writeFile('result.txt', data1 + data2);
console.log('操作完成');
} catch (err) {
console.error('出错:', err);
}
}
4. 事件驱动示例
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
// 设置事件监听器(一种特殊的回调)
myEmitter.on('event', (arg1, arg2) => {
console.log('事件触发,参数:', arg1, arg2);
});
// 触发事件
myEmitter.emit('event', '参数1', '参数2');
5. 实用技巧
回调函数柯里化
function createLogger(prefix) {
return function(err, result) {
const timestamp = new Date().toISOString();
if (err) {
console.error(`[${timestamp}] ${prefix} 错误:`, err.message);
} else {
console.log(`[${timestamp}] ${prefix} 结果:`, result);
}
};
}
fs.readFile('data.txt', 'utf8', createLogger('文件读取'));
回调函数封装
function promisify(fn) {
return function(...args) {
return new Promise((resolve, reject) => {
fn(...args, (err, result) => {
if (err) reject(err);
else resolve(result);
});
});
};
}
// 将回调函数转换为 Promise
const readFileAsync = promisify(fs.readFile);
readFileAsync('file.txt', 'utf8')
.then(data => console.log(data))
.catch(err => console.error(err));
关键要点
错误优先:回调函数的第一个参数总是错误对象
异步执行:回调函数在事件循环的后续阶段执行
避免阻塞:回调函数中不要执行同步阻塞操作
现代替代方案:对于新项目,推荐使用 Promise 或 async/await
虽然回调函数是 Node.js 的基础,但在现代开发中,通常建议使用 Promise 或 async/await 语法来处理异步操作,以提高代码的可读性和可维护性。