谢晋杰
谢晋杰
发布于 2025-09-15 / 19 阅读
0
0

什么是回调函数(Callback)

回调函数是很多程序员曾有疑问的一个概念,最近又回顾了下这个概念,看到大概是条费鱼no.body的回答又有新的收获,自己也重新整理一下。

1️⃣概念

回调函数也是一个函数,形式上和一般函数相同。

比如,下面给出一个求和函数sum(),这里是没办法区分这个函数是一般函数还是回调函数。

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

回调函数与一般函数的区别是,回调函数是被当做一个参数传递给其他函数,供其调用;而一般函数是在需要的地方直接调用。

比如,下面添加了一个计算函数calculate(),将求和函数sum()作为参数operate传入,这时,求和函数sum()就是一个回调函数了。

def sum(a, b):
    return a + b
def calculate(a, b, operate):
    return operate(a, b)
def main():
    i = calculate(2, 3, sum)
    print(i)
if __name__ == "__main__":
    main()

其实,上面回调过程有三个部分:

  1. 起始函数:确定使用的回调函数。对应上面的main函数。

  2. 库函数(中间函数):需要传入回调函数参数的函数。对应上面的calculate函数。

  3. 回调函数:被当做一个参数的函数。对应上面的sum函数。

下图是他们的关系(图片来源:维基百科):

2️⃣用途

那为什么要有回调函数呢,为什么不用一般函数?

回调函数可以将调用权限转移给其它函数或者系统。

这样其它函数或者系统就可以根据实际情况进行调用,比如延时调用。

就好像你去商店买东西,但是没货了,你将电话留给老板,等有货了老板再联系(callback)你。

做框架代码或者底层代码会经常遇到这样的情况,具体的逻辑需要上层应用提供,这时就需要给上层暴露接口或提供回调函数参数。

其实,接口和回调函数有一定的相通性。

在Java最开始中没有回调函数这个概念,相应的功能都是通过接口实现的。到Java8函数式编程之后才有这个概念。

接口是多方法回调的正式契约版本,回调函数是单方法接口的轻量版本。

回调函数多用于以下场景:

  • 事件处理

回调函数可用于事件处理。向系统特定类型的事件注册回调函数,当事件发生时系统调用对应函数。

比如订单页面上的下单按钮,用户点击后执行处理下单业务的回调函数。

  • 异步操作

回调函数可用于实现异步处理。异步执行某个操作,并提供一个在操作完成时被调用的回调函数。

比如商品页面向后台发起数据请求,等请求成功后执行渲染数据的回调函数。

  • 多态性

通过提供不同实现的回调函数,可以实现调用函数的多态性。

之前买东西的比喻,你还可以将地址留个老板,等有货了老板送货上门。

同理,下面在添加了新的回调函数multiply(),向计算函数calculate()注入不同的回调函数,就可以实现不同的功能。

def sum(a, b):
    return a + b
    
def multiply(a, b):
    return a * b

def calculate(a, b, operate):
    return operate(a, b)

def main():
    i = calculate(2, 3, sum)
    print(i)
    i = calculate(2, 3, multiply)
    print(i)

if __name__ == "__main__":
    main()

3️⃣阻塞(同步)回调和非阻塞(异步)回调

还有两个概念,阻塞(同步)回调和非阻塞(异步)回调。

其实,这两个也很好分别:

如果起始函数和回调函数在同一线程就是阻塞(同步)回调;

如果起始函数和回调函数不在同一线程就是非阻塞(异步)回调。

参考资料


评论