回调函数(Callback Function)是一种作为参数传递给其他函数的函数,当特定事件发生或某个操作完成时,这个函数会被"回调"执行。
基本概念
回调函数的核心思想是控制反转 - 你把一个函数交给别人调用,而不是自己直接调用。
工作原理
// 简单示例
function doSomething(callback) {
console.log("执行某个操作...");
// 操作完成后调用回调函数
callback();
}
// 定义回调函数
function myCallback() {
console.log("回调函数被调用了!");
}
// 传递回调函数
doSomething(myCallback);常见应用场景
异步操作:AJAX请求、文件读写、定时器
事件处理:点击事件、键盘事件
数组方法:
forEach(),map(),filter()
// 事件回调
button.addEventListener('click', function() {
console.log('按钮被点击了');
});
// 数组方法回调
[1, 2, 3].forEach(function(item) {
console.log(item);
});
[1, 2, 3].forEach(item => console.log(item));
为什么这是回调函数?
函数作为参数:箭头函数被传递给 forEach
被别人调用:forEach 内部会调用这个函数
控制反转:你定义函数,但由 forEach 决定何时调用什么是回调地狱
回调地狱(Callback Hell)是指当多个异步操作需要按顺序执行时,回调函数层层嵌套,导致代码难以阅读和维护的情况。
回调地狱的典型特征:
多层嵌套的回调函数
代码向右缩进形成"金字塔"
逻辑难以追踪
错误处理复杂
// 经典的回调地狱示例
fs.readFile('file1.txt', function(err, data1) {
if (err) throw err;
fs.readFile('file2.txt', function(err, data2) {
if (err) throw err;
fs.readFile('file3.txt', function(err, data3) {
if (err) throw err;
console.log(data1, data2, data3);
// 可能还有更多嵌套...
});
});
});解决方案
1. Promise
function readFilePromise(filename) {
return new Promise((resolve, reject) => {
fs.readFile(filename, (err, data) => {
if (err) reject(err);
resolve(data);
});
});
}
// 使用Promise链
readFilePromise('file1.txt')
.then(data1 => {
return readFilePromise('file2.txt');
})
.then(data2 => {
return readFilePromise('file3.txt');
})
.then(data3 => {
console.log('所有文件读取完成');
})
.catch(err => {
console.error('出错:', err);
});2. Async/Await(推荐)
async function readFiles() {
try {
const data1 = await readFilePromise('file1.txt');
const data2 = await readFilePromise('file2.txt');
const data3 = await readFilePromise('file3.txt');
console.log('所有文件读取完成');
} catch (err) {
console.error('出错:', err);
}
}
readFiles();3. 模块化分离
// 将每个操作封装成独立函数
function processFile1(callback) {
fs.readFile('file1.txt', callback);
}
function processFile2(callback) {
fs.readFile('file2.txt', callback);
}
function processFile3(callback) {
fs.readFile('file3.txt', callback);
}
// 使用命名函数代替匿名函数
function handleFile1(err, data1) {
if (err) throw err;
processFile2(handleFile2);
}
function handleFile2(err, data2) {
if (err) throw err;
processFile3(handleFile3);
}
function handleFile3(err, data3) {
if (err) throw err;
console.log('处理完成');
}
processFile1(handleFile1);4. 使用async库
const async = require('async');
async.series([
callback => fs.readFile('file1.txt', callback),
callback => fs.readFile('file2.txt', callback),
callback => fs.readFile('file3.txt', callback)
], (err, results) => {
if (err) throw err;
console.log('所有文件:', results);
});最佳实践
优先使用 async/await:代码最清晰,错误处理简单
合理使用 Promise:兼容性好,支持链式调用
避免深层嵌套:超过3层嵌套时考虑重构
统一错误处理:在顶层集中处理错误
函数命名:给回调函数起有意义的名字