函数回调的本质和原理

目录


函数回调的定义:

通俗地讲,把一个函数作为参数传给另一个函数,这个函数则称为回调函数。

图解:

在看看严格点的定义:

函数回调就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

下文的函数回调、回调函数是一个意思。


把函数当参数

把函数当做参数的好处是?

假设实现一个计算器的需求,先编写三个功能函数:求两个整数的求最大值,求最小值,求和。

python 复制代码
def computer(a, b, func):
    return func(a, b)


def max(a, b):
    return [a, b][a < b]


def min(a, b):
    return [a, b][a > b]


def sum(a, b):
    return str(int(a) + int(b))


if __name__ == "__main__":
    a = input("请输入整数a:")
    b = input("请输入整数b:")

    res = computer(a, b, max)
    print("Max of " + a + " and " + b + " is " + res)

    res = computer(a, b, min)
    print("Min of " + a + " and " + b + " is " + res)

    res = computer(a, b, sum)
    print("Sum of " + a + " and " + b + " is " + res)
text 复制代码
请输入整数a:2
请输入整数b:3
Max of 2 and 3 is 3
Min of 2 and 3 is 2
Sum of 2 and 3 is 5

但是这个包将作为SDK给别人使用的话,是不知道别人想搭配什么功能函数的。那么可以将这三个函数都作为实参,让用户自由传入,这样就增加了编程的灵活性

在调用max、min、sum时,这三个函数就是此处的回调函数。(回调函数和普通函数在定义的时候没有什么区别,只有在调用时才看出来是不是回调函数,正常调用就是普通函数,作为一个函数的参数在需要的时候分情况调用,就是回调函数。)


可以异步的函数

当然回调函数还有一个更大的作用,就是可以结合上下文 做异步,好处是异步不阻塞

需要说明一下,回调的异步性并非来自函数本身,而是由调用它的API或操作(如setTimeout、I/O、事件监听)决定的。这种设计使得程序能在等待耗时操作时不阻塞主线程,从而提升效率和用户体验。理解事件循环和任务队列机制是掌握异步编程的关键。

异步不是本文的核心,这里只看看回调怎么结合异步实现一些超出同步编程的效果就好。

异步在前端编程中很常用,下面通过一个 前端开发场景 来展示异步回调如何解决同步代码无法处理的问题:避免界面冻结,同时执行耗时任务


案例背景:模拟文件上传

假设我们正在开发一个网页,用户点击按钮后需要:

  1. 上传一个大文件到服务器(耗时操作)。
  2. 上传完成后显示"上传成功"。
  3. 同时,用户在上传过程中可以继续操作页面(比如输入文字、点击其他按钮)。

同步代码的问题

如果用同步代码实现文件上传,会阻塞主线程,导致界面完全卡死,用户无法进行任何操作:

javascript 复制代码
// 同步上传函数(假设存在同步的 uploadSync API)
function uploadSync(file) {
  // 模拟耗时操作(假设上传需要3秒)
  const start = Date.now();
  while (Date.now() - start < 3000) {} // 同步阻塞3秒
  return "上传成功";
}

// 点击按钮触发上传
document.getElementById("uploadBtn").addEventListener("click", () => {
  console.log("开始上传...");
  const result = uploadSync("bigfile.zip"); // 同步调用,阻塞主线程3秒
  console.log(result);
  document.getElementById("status").textContent = result;
});

// 用户尝试在上传过程中输入文字,但界面会卡住3秒!

问题

  • 上传期间,用户无法在输入框打字,所有UI操作被冻结。

  • 控制台输出顺序是:

    复制代码
    开始上传...
    (3秒后)
    上传成功

异步回调解决方案

改用异步回调,释放主线程,让用户在上传过程中继续操作页面:

javascript 复制代码
// 异步上传函数(使用回调)
function uploadAsync(file, callback) {
  console.log("开始上传...");
  // 使用 setTimeout 模拟异步上传(如真实的 fetch 或 XMLHttpRequest)
  setTimeout(() => {
    const result = "上传成功";
    callback(result); // 上传完成后调用回调
  }, 3000);
}

// 点击按钮触发上传
document.getElementById("uploadBtn").addEventListener("click", () => {
  uploadAsync("bigfile.zip", (result) => {
    console.log(result);
    document.getElementById("status").textContent = result;
  });
});

// 用户可以在上传过程中正常输入文字!

关键区别

  • 上传期间,用户可以在输入框自由输入,界面保持响应。

  • 控制台输出顺序是:

    复制代码
    开始上传...
    (立即输出,不阻塞)
    (3秒后)
    上传成功

流程图解

复制代码
[用户点击上传按钮]
│
├─ 主线程执行:调用 uploadAsync
│  │
│  ├─ 1. 输出 "开始上传..."
│  │
│  ├─ 2. 启动异步操作(setTimeout 3秒)
│  │   │
│  │   └─ (3秒后)执行回调:更新界面状态
│  │
│  └─ 3. 函数立即返回,主线程空闲
│
├─ 用户立即可以操作页面(输入文字、点击其他按钮)
│
└─ 3秒后,回调触发,更新界面

为什么异步回调解决了同步无法处理的问题?

  1. 非阻塞主线程

    • 同步代码会独占主线程,导致浏览器无法处理用户输入、动画渲染等任务。
    • 异步回调将耗时任务交给浏览器底层API(如网络线程、定时器线程),主线程继续响应用户操作。
  2. 保持用户体验

    • 用户在上传文件时,仍可以与其他UI元素交互(如填写表单、切换标签页)。
  3. 真实场景应用

    • 所有Web应用的网络请求(如AJAX、Fetch API)、文件读写(Node.js)、数据库操作都必须使用异步,否则会导致服务完全卡死。

实际开发中会用到的异步回调

真实项目中,异步回调常用于:

javascript 复制代码
// 1. 网络请求
fetch("/api/data")
  .then(response => response.json())
  .then(data => console.log(data));

// 2. 定时任务
setTimeout(() => console.log("延时操作"), 1000);

// 3. 用户事件监听
document.getElementById("button").addEventListener("click", () => {
  console.log("按钮被点击");
});

// 4. Node.js 文件读取
const fs = require("fs");
fs.readFile("file.txt", "utf8", (err, data) => {
  if (err) throw err;
  console.log(data);
});

以上场景一般也都是 回调函数 + 异步实现。


总结

  • 同步代码的问题:阻塞主线程,导致界面冻结、用户体验极差。
  • 异步回调的优势
    • 主线程保持响应,用户可以继续操作。
    • 充分利用硬件资源(如多线程、非阻塞I/O)。
    • 适用于所有耗时操作(网络、I/O、复杂计算)。

这也是现代Web开发中,异步回调(及其衍生技术如Promise、Async/Await)是必须掌握的核心概念!

有兴趣的同学,可以更深入地去了解系统底层都做了什么,才能达成异步效果。 Bye !

相关推荐
gqkmiss16 小时前
AbortController:让异步操作随时说停就停
前端·async·异步·steam·abortcontroller
Amd7941 天前
FastAPI依赖注入性能优化策略
单例模式·性能优化·fastapi·依赖注入·错误处理·异步编程·缓存机制
Amd7945 天前
FastAPI中的依赖注入与数据库事务管理
fastapi·依赖注入·sqlalchemy·事务管理·异步编程·sql注入防护·数据库会话管理
茶本无香7 天前
Java异步编程中的CompletableFuture介绍、常见错误及最佳实践
java·future·异步·常见错误
陌言不会python15 天前
谷粒微服务高级篇学习笔记整理---异步&线程池
笔记·学习·微服务·线程池·异步
海晨忆20 天前
JS—异步编程:3分钟掌握异步编程
开发语言·javascript·ecmascript·异步编程·并发的底层原理
长安er21 天前
异步编程与流水线架构:从理论到高并发
数学建模·架构·gui·多线程·异步·流水线·全息
亿牛云爬虫专家21 天前
数据分析异步进阶:aiohttp与Asyncio性能提升
数据分析·爬虫代理·异步·asyncio·aiohttp·今日头条·www.toutiao.com
不能只会打代码1 个月前
六十天前端强化训练之第十四天之深入理解JavaScript异步编程
开发语言·前端·javascript·异步编程