JS-回调函数

JS-回调函数

_

回调函数(Callback Function)是一种作为参数传递给其他函数的函数,当特定事件发生或某个操作完成时,这个函数会被"回调"执行。

基本概念

回调函数的核心思想是控制反转 - 你把一个函数交给别人调用,而不是自己直接调用。

工作原理

// 简单示例
function doSomething(callback) {
    console.log("执行某个操作...");
    // 操作完成后调用回调函数
    callback();
}

// 定义回调函数
function myCallback() {
    console.log("回调函数被调用了!");
}

// 传递回调函数
doSomething(myCallback);

常见应用场景

  1. 异步操作:AJAX请求、文件读写、定时器

  2. 事件处理:点击事件、键盘事件

  3. 数组方法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);
});

最佳实践

  1. 优先使用 async/await:代码最清晰,错误处理简单

  2. 合理使用 Promise:兼容性好,支持链式调用

  3. 避免深层嵌套:超过3层嵌套时考虑重构

  4. 统一错误处理:在顶层集中处理错误

  5. 函数命名:给回调函数起有意义的名字

Vue路由懒加载 2025-03-13
Vue 3 Pinia状态管理 2025-01-18

评论区