回调函数
Posted 四季帆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了回调函数相关的知识,希望对你有一定的参考价值。
0. 前言
这篇文章是我以前看别人的微信公众号推文做下的记录,时间有点儿久了,找不到原文链接了,因为现在发表为转载的话需要填写原链接才能发表,所以请原谅我发表为了“原创”。
1. 定义
在计算机科学中,回调函数是指一段以参数的形式传递给其它代码的可执行代码。
2. 形象理解:明日油条项目
2.1 假设我们“明日油条”App代码第一版是这样写的:意思很简单,制作好油条后卖出去。
make_youtiao(10000);
sell();
2.2 “明日油条”App代码第二版,使用回调的方法,第二个被make_youtiao函数调用的函数就叫回调,callback。
void make_youtiao(int num, func call_back) {
// 制作油条
call_back(); //执行回调
}
2.3 为什么编写make_youtiao这个小组不直接定义sell函数然后调用呢?
关键:明日油条这个App是由A小组和B小组同时开发的,A小组在编写make_youtiao时怎么知道B小组要怎么用这个模块,假设A小组真的自己定义sell函数就会这样写:
void make_youtiao(int num) {
real_make_youtiao(num);
sell(); //执行回调
}
2.4 同时A小组设计的模块非常好用,这时C小组也想用这个模块,然而C小组的需求是制作完油条后放到仓库而不是不是直接卖掉,要满足这一需求那么A小组该怎么写呢?
void make_youtiao(int num) {
real_make_youtiao(num);
if (Team_B) {
sell(); // 执行回调
} else if (Team_C) {
store(); // 放到仓库
}
}
2.5 故事还没完,假设这时D小组又想使用呢,难道还要接着添加if else吗?这样的话A小组的同学只需要维护make_youtiao这个函数就能做到工作量饱满了,显然这是一种非常糟糕的设计。
2.6 结论:制作完油条后接下来该做什么不是实现make_youtiao的A小组该关心的事情,很明显只有调用make_youtiao这个函数的使用方才知道。因此make_youtiao的A小组完全可以通过回调函数将接下来该干什么交给调用方实现,A小组的同学只需要针对回调函数这一抽象概念进行编程就好了,这样调用方在制作完油条后不管是卖掉、放到库存还是自己吃掉等等想做什么都可以,A小组的make_youtiao函数根本不用做任何改动,因为A小组是针对回调函数这一抽象概念来编程的。
3. 异步回调
3.1 同步调用
void make_youtiao(int num, func call_back) {
real_make_youtiao(num);
call_back(); //执行回调
}
以上功能实现有一个问题,那就是make_youtiao函数的调用方式依然是同步的,也就是说调用方是这样实现的:
make_youtiao(10000, sell);
// make_youtiao函数返回前什么都做不了
由于我们要制作10000个油条,make_youtiao函数执行完需要10分钟,调用方依然会被阻塞10分钟,这就是同步调用的问题所在
3.2 异步调用
反正调用方并不关心make_youtiao这一函数的返回值,那么一种更好的方式是:把制作油条的这一任务放到另一个线程(进程)、甚至另一台机器上。如果用线程实现的话,那么make_youtiao就是这样实现了:
void make_youtiao(int num, func call_back) {
// 在新的线程中执行处理逻辑
create_thread(real_make_youtiao, num, call_back);
}
这时当我们调用make_youtiao时就会立刻返回,即使油条还没有真正开始制作,而调用方也完全无需等待制作油条的过程,可以立刻执行后续流程:
make_youtiao(10000, sell);
// 立刻返回
// 执行后续流程
这时调用方的后续流程可以和制作油条同时进行,这就是函数的异步调用,当然这也是异步的高效之处。
4. 回调地狱
异步回调这种机制和程序员最熟悉的同步模式不一样,在可理解性上比不过同步,而如果业务逻辑相对复杂,比如我们处理某项任务时不止需要调用一项服务,而是几项甚至十几项,如果这些服务调用都采用异步回调的方式来处理的话,那么很有可能我们就陷入回调地狱中。
举个例子,假设处理某项任务我们需要调用四个服务,每一个服务都需要依赖上一个服务的结果,如果用同步方式来实现的话可能是这样的:
a = GetServiceA();
b = GetServiceB(a);
c = GetServiceC(b);
d = GetServiceD(c);
代码很清晰,很容易理解有没有。
我们知道异步回调的方式会更加高效,那么使用异步回调的方式来写将会是什么样的呢?
GetServiceA(function(a){
GetServiceB(a, function(b){
GetServiceC(b, function(c){
GetServiceD(c, function(d) {
....
});
});
});
});
以上是关于回调函数的主要内容,如果未能解决你的问题,请参考以下文章