前言
不知道你是否有这样的疑问,我们为什么需要回调函数,直接调用函数不就可以了吗?回调函数到底有什么用?程序员该如何理解回调函数了?
这篇文章就为你解答这些问题,读完这篇文章后,你的编程技术将得到质的提高。
同步调用
做一件事情,程序员传统并且最熟悉思维模式一般是这样:
- 调用某个函数,获取结果
- 处理获取到的结果
result = request()
handles(result)
如果 request()函数是同步调用,只有request()函数返回拿到结果后,才能调用handle函数进行处理,request()函数返回前我们必须等待,这就是同步调用,控制流程就是这样的
异步调用
但是如果我们想更加高效的话,那么就需要异步调用了,我们不去直接调用handle函数,而是作为参数传递给 request()
request(handle)
所以,我们根本不关心request什么时候真正的获取结果,这是request该关心的事情,我们只需要把获取到的结果,该怎么处理告诉request就可以了,因此 request函数可以立即返回,真正获取结果是可能在另一个线程,进程,甚至另一台机器上完成,这就是异步调用。
从编程思维上看,异步调用和同步有很大的差别,如果我们把处理流程当做一个任务来的话,那么同步流程下,整个任务都是我们实现的,但是异步流程情况下:任务的处理流程被分为两部分:
- 第一部分,我们处理,也就是调用 request函数之前的部分。
- 第二部分:不是我们处理的,而是在其他线程、进程或者另一台机器完成
重点来了:
第二部分的调用不在我们控制范围内,同时只有调用方才知道该做什么,什么时候调用,因此在这种情况下,就需要一种必要的机制-------》这个机制就是回调。
- 在java/Android中,我们通常定义一个接口,然后我们实现这个接口,将接口类型作为参数传给服务方,然后服务方自己决定什么时候去调用这个接口的函数(这个接口函数是我们客户端实现的).
- 在C++中没有Java这种类似的接口函数回调,但是C++中有函数指针这个概念,它可以做为参数传给服务端,同样也可以起到和 java类似的功能效果。
回调函数正式定义
回调函数就是一个函数和其他函数没有任何区别。
一般函数:我们自己编写函数会在自己的程序内调用,也就是说函数的编写放方式我们自己,调用方也是我们自己
回调函数:编写方是我们自己,但是函数调用方不是我们,而是我们引用的其他模块,也就是三方库,我们调用第三方库中的函数,并把回调函数传递给第三方库,第三方库中的函数调用我们编写的函数。
回调函数实际应用:(回调函数作为事件处理函数)
- 如上图所示:我们抽象出这样一个模型:主应用程序--------库函数--------回调函数。库函数是不知道某个节点(或者业务完成之后在去做什么)。
- 例如接收到 网络数据、文件读取完成之后,该做什么这些只有库的使用者才知道,设计编写库的作者并不知道,也无法针对主应用程序编写具体代码,只能对外提供一个回调函数(声明一个回调函数),库的使用方来实现该函数(定义该函数),库在特定的时机,特定的节点回调该函数就可以了。
- 我们在抽象一下:如果把接收网络数据,文件读取看做是事件也就是 event, 那么本质上我们编写的回调函数就是用来处理Event的,因此从这个角度看回调函数不过就是 event handler ,因此回调函数天然适用于 事件驱动编程 event--driven 。
同步回调
- 这种回到就是常说的同步回调 synchronous callbacks 、也有将其称为阻塞式回调 blocking callbacks ,或者什么修饰都没有,就是回调callback ,这是我们最熟悉 的回调方式。
- 当我们调用某个函数A并以参数的形式 传入回调函数后,在A返回之前 回调函数会 被执行,也就是 说我们的主程序会等待回调函数执行完成,这就是同步回调。
异步回调
- 不同于同步回调,当我们调用某个函数A并以参数的形式传入回调函数后,A函数立即返回,也就是函数A并不会阻塞我们的主程序,一段时间后回调函数开始被执行,此时我们的主程序可能在忙其他任务,回调函数的执行 和我们主程序的运行同时进行。
- 既然我们的主程序和回调函数的执行可以同时发生 ,因此一般情况下,主程序和回调函数的执行位于不同的线程或者进程中。
这就是所谓的异步回调,asynchronous callbacks ,也有资料称为 deferred callbacks ,名字很形象,延迟回调。
回调对应的编程思维模式
- 假设我们向处理某项任务,这项任务需要依赖某项服务S,我们可以将任务的处理分为两部分,调用服务S的前部分 PA ,调用服务S后面部分为 PB。
- 常规模式 下,PA和PB都是服务调用方执行,也就是我们自己的主程序执行 PA,等待服务S返回后再执行PB部分。
- 回调模式下:PA是服务调用方执行,也就是我们自己 的主程序执行PA,等待服务S返回后,服务S在去调用PB.
回调函数实作
回调函数步骤:
1、定义一个函数(普通函数即可);
2、将此函数的地址注册给调用者;
3、特定的事件或条件发生时,调用者使用函数指针调用回调函数。
// main.cpp
#include<iostream>
// 定义一个函数指针:返回值 int ,需要两个int 类型参数
typedef int (*Ptr)(int, int);
// 2:注册函数
int RegFuncation_add(Ptr pInt, int a, int b) {
// 调用回调函数
return pInt(a,b);
}
class OperMath
{
public:
// 1: 回调函数
static int add(int a, int b) {
return a + b;
}
// 1: 回调函数
static int reduce(int a, int b) {
return a - b;
}
// 3: 注册函数
void RegFuncationCallBack_add(int a, int b) {
std::cout << RegFuncation_add(add, a, b) << std::endl;
}
// 3: 注册函数
void RegFuncationCallBack_reduce(int a,int b) {
std::cout << RegFuncation_add(reduce, a, b) << std::endl;
}
};
int main() {
OperMath math;
math.RegFuncationCallBack_add(1, 2); // 打印:3
math.RegFuncationCallBack_reduce(2, 1); // 打印:1
}