找回密码
 立即注册
首页 业界区 业界 函数回调的本质和原理

函数回调的本质和原理

濮阳雅爱 2025-6-2 22:43:11
目录

  • 把函数当参数
  • 可以异步的函数

    • 案例背景:模拟文件上传
    • 同步代码的问题
    • 异步回调解决方案
    • 为什么异步回调解决了同步无法处理的问题?
    • 实际开发中会用到的异步回调
    • 总结


函数回调的定义:
通俗地讲,把一个函数作为参数传给另一个函数,这个函数则称为回调函数。
图解:
正常函数的模型图
1.png

函数回调的模型图
2.png

在看看严格点的定义:
函数回调就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。(新人可能会云里雾里,没关系,结合着案例多看看。)
下文的函数回调、回调函数是一个意思。
把函数当参数

把函数当做参数的好处是?
假设实现一个计算器的需求,先编写三个功能函数:求两个整数的求最大值,求最小值,求和。
  1. def computer(a, b, func):
  2.     return func(a, b)
  3. def max(a, b):
  4.     return [a, b][a < b]
  5. def min(a, b):
  6.     return [a, b][a > b]
  7. def sum(a, b):
  8.     return str(int(a) + int(b))
  9. if __name__ == "__main__":
  10.     a = input("请输入整数a:")
  11.     b = input("请输入整数b:")
  12.     res = computer(a, b, max)
  13.     print("Max of " + a + " and " + b + " is " + res)
  14.     res = computer(a, b, min)
  15.     print("Min of " + a + " and " + b + " is " + res)
  16.     res = computer(a, b, sum)
  17.     print("Sum of " + a + " and " + b + " is " + res)
复制代码
  1. 请输入整数a:2
  2. 请输入整数b:3
  3. Max of 2 and 3 is 3
  4. Min of 2 and 3 is 2
  5. Sum of 2 and 3 is 5
复制代码
但是这个包将作为SDK给别人使用的话,是不知道别人想搭配什么功能函数的。那么可以将这三个函数都作为实参,让用户自由传入,这样就增加了编程的灵活性
在调用max、min、sum时,这三个函数就是此处的回调函数。(回调函数和普通函数在定义的时候没有什么区别,只有在调用时才看出来是不是回调函数,正常调用就是普通函数,作为一个函数的参数在需要的时候分情况调用,就是回调函数。)
可以异步的函数

当然回调函数还有一个更大的作用,就是可以结合上下文做异步,好处是异步不阻塞
需要说明一下,回调的异步性并非来自函数本身,而是由调用它的API或操作(如setTimeout、I/O、事件监听)决定的。这种设计使得程序能在等待耗时操作时不阻塞主线程,从而提升效率和用户体验。
异步是系统底层封装好的功能,大致是通过对事件循环和任务队列机制等一起打包好了,只暴露一些公开函数和关键字给使用者。使用者只需要设计好一个代码块、一个函数传入异步即可,所以函数回调和异步就这样结合起来了。
异步不是本文的核心,这里只看看回调怎么结合异步实现一些超越同步编程的效果就好。
异步在前端编程中很常用,下面通过一个前端开发场景来展示异步回调如何解决同步代码无法处理的问题:避免界面冻结,同时执行耗时任务
案例背景:模拟文件上传

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

  • 上传一个大文件到服务器(耗时操作)。
  • 上传完成后显示“上传成功”。
  • 同时,用户在上传过程中可以继续操作页面(比如输入文字、点击其他按钮)。
同步代码的问题

如果用同步代码实现文件上传,会阻塞主线程,导致界面完全卡死,用户无法进行任何操作:
  1. // 同步上传函数(假设存在同步的 uploadSync API)
  2. function uploadSync(file) {
  3.   // 模拟耗时操作(假设上传需要3秒)
  4.   const start = Date.now();
  5.   while (Date.now() - start < 3000) {} // 同步阻塞3秒
  6.   return "上传成功";
  7. }
  8. // 点击按钮触发上传
  9. document.getElementById("uploadBtn").addEventListener("click", () => {
  10.   console.log("开始上传...");
  11.   const result = uploadSync("bigfile.zip"); // 同步调用,阻塞主线程3秒
  12.   console.log(result);
  13.   document.getElementById("status").textContent = result;
  14. });
  15. // 用户尝试在上传过程中输入文字,但界面会卡住3秒!
复制代码
问题

  • 上传期间,用户无法在输入框打字,所有UI操作被冻结。
  • 控制台输出顺序是:
    1. 开始上传...
    2. (3秒后)
    3. 上传成功
    复制代码
异步回调解决方案

改用异步回调,释放主线程,让用户在上传过程中继续操作页面:
  1. // 异步上传函数(使用回调)
  2. function uploadAsync(file, callback) {
  3.   console.log("开始上传...");
  4.   // 使用 setTimeout 模拟异步上传(如真实的 fetch 或 XMLHttpRequest)
  5.   setTimeout(() => {
  6.     const result = "上传成功";
  7.     callback(result); // 上传完成后调用回调
  8.   }, 3000);
  9. }
  10. // 点击按钮触发上传
  11. document.getElementById("uploadBtn").addEventListener("click", () => {
  12.   uploadAsync("bigfile.zip", (result) => {
  13.     console.log(result);
  14.     document.getElementById("status").textContent = result;
  15.   });
  16. });
  17. // 用户可以在上传过程中正常输入文字!
复制代码
  1. setTimeout()函数介绍:函数接受两个参数,第一个是回调函数,第二个是推迟执行的毫秒数。
  2. setTimeout()会将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。 要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。
复制代码
关键区别

  • 上传期间,用户可以在输入框自由输入,界面保持响应。
  • 控制台输出顺序是:
    1. 开始上传...
    2. (立即输出,不阻塞)
    3. (3秒后)
    4. 上传成功
    复制代码
流程图解
  1. [用户点击上传按钮]
  2. ├─ 主线程执行:调用 uploadAsync
  3. │  │
  4. │  ├─ 1. 输出 "开始上传..."
  5. │  │
  6. │  ├─ 2. 启动异步操作(setTimeout 3秒)
  7. │  │   │
  8. │  │   └─ (3秒后)执行回调:更新界面状态
  9. │  │
  10. │  └─ 3. 函数立即返回,主线程空闲
  11. ├─ 用户立即可以操作页面(输入文字、点击其他按钮)
  12. └─ 3秒后,回调触发,更新界面
复制代码
为什么异步回调解决了同步无法处理的问题?


  • 非阻塞主线程

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

  • 保持用户体验

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

  • 真实场景应用

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

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

真实项目中,异步回调常用于:
  1. // 1. 网络请求
  2. fetch("/api/data")
  3.   .then(response => response.json())
  4.   .then(data => console.log(data));
  5. // 2. 定时任务
  6. setTimeout(() => console.log("延时操作"), 1000);
  7. // 3. 用户事件监听
  8. document.getElementById("button").addEventListener("click", () => {
  9.   console.log("按钮被点击");
  10. });
  11. // 4. Node.js 文件读取
  12. const fs = require("fs");
  13. fs.readFile("file.txt", "utf8", (err, data) => {
  14.   if (err) throw err;
  15.   console.log(data);
  16. });
复制代码
以上场景一般也都是 回调函数 + 异步实现。
总结


  • 同步代码的问题:阻塞主线程,导致界面冻结、用户体验极差。
  • 异步回调的优势

    • 主线程保持响应,用户可以继续操作。
    • 充分利用硬件资源(如多线程、非阻塞I/O)。
    • 适用于所有耗时操作(网络、I/O、复杂计算)。

这也是现代Web开发中,异步回调(及其衍生技术如Promise、Async/Await)是必须掌握的核心概念!
有兴趣的同学,可以更深入地研究一下系统底层都做了什么,才能达成异步效果。 Bye !

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册