一、什么是回调函数
回调函数是一种在编程中常见的概念,它是将一个函数作为参数传递给另一个函数(函数指针,函数的首地址),并在特定事件发生或特定条件满足时被调用执行的函数。
以下是我对回调函数的简化的理解。
二、回调函数的例子
因为在不同的语言中回调函数的写法不同,因此下面采用伪代码作为例子:
//下面的“CallFunction”是普通函数
FunctionA(){
CallFunction();
}
//下面的“CallFunction”是无参回调函数
FunctionA(CallFunction){
CallFunction();
}
//下面的“CallFunction”是有参回调函数,参数是Parameter
FunctionA(CallFunctionWithParameter,Parameter){
CallFunctionWithParameter(Parameter);
}
为了加深对回调函数的理解,再举几个编程过程中随处可见的回调函数作为例子:
- Java的函数式接口,因为java都要封装成类,所以有了这种只有一个方法的类,用于方便进行回调函数的编写;
- JS的通过事件触发的回调函数;
- Python中的高阶函数,可以通过传入函数进行调用来实现不同的功能;
- Servlet中调用的init()、service()、destroy()等生命周期方法;
- Vue中调用的created()等8个生命周期钩子函数。
三、为什么要使用回调函数
那么回调函数相比于普通函数的直接调用有什么优点,我们为什么要使用回调函数呢?
-
编码更加灵活:利用回调函数使得底层代码能够间接调用高层定义的函数或子程序,回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
一般的调用是高层的函数(用户自己写的函数)调用底层的函数(框架或基础包提供的函数),但是回调函数恰恰相反,它是底层定义好的方法去调用高层的函数(用户自己写的函数),比如Servlet的生命周期函数反过来调用了用户写的service()方法;另外,可能会质疑不一定非要用“回调函数”来实现“底层函数调用高层函数”,也可以用函数重写来实现类似的功能,用户可以重写预先定义好的函数作为高层函数。然而,这种函数重写通常依赖于全局状态或动态绑定,它可能不如使用回调函数那样明确和灵活。
-
异步操作与事件驱动:利用回调函数可以方便的实现异步操作和事件驱动,因为回调函数天然就创建了一个函数执行的入口(进程和线程的执行都需要传入一个函数指针,用来记录线程/进程开始执行的位置)。因此回调函数经常传入创建线程的方法,实现异步操作。事件驱动也是同理使用了回调函数。
当然,也不是回调函数都是异步的,也有同步回调函数,即回调函数使用在同步(需要等待执行)的场景中,只是这种方式完全可以使用普通函数来实现,因此回调函数往往都和异步的概念联系在一起,但是其实回调函数和异步是两个不同纬度的概念。