res = request();handle(res);
这就是函数的同步调用,只有request()函数返回拿到结果后,才能调用handle函数进行处理,request函数返回前我们必须等待,这就是同步调用,其控制流是这样的:
文章插图
但是如果我们想更加高效的话,那么就需要异步调用了,我们不去直接调用handle函数,而是作为参数传递给request:
request(handle);
我们根本就不关心request什么时候真正的获取的结果,这是request该关心的事情,我们只需要把获取到结果后该怎么处理告诉request就可以了,因此request函数可以立刻返回,真的获取结果的处理可能是在另一个线程、进程、甚至另一台机器上完成 。这就是异步调用,其控制流是这样的:
文章插图
从编程思维上看,异步调用和同步有很大的差别,如果我们把处理流程当做一个任务来的话,那么同步下整个任务都是我们来实现的,但是异步情况下任务的处理流程被分为了两部分:
- 第一部分是我们来处理的,也就是调用request之前的部分
- 第二部分不是我们处理的,而是在其它线程、进程、甚至另一个机器上处理的 。
也就是说回调函数的本质就是“只有我们才知道做些什么,但是我们并不清楚什么时候去做这些,只有其它模块才知道,因此我们必须把我们知道的封装成回调函数告诉其它模块” 。
现在你应该能看出异步回调这种编程思维模式和同步的差异了吧 。
接下来我们给回调一个较为学术的定义
正式定义在计算机科学中,回调函数是指一段以参数的形式传递给其它代码的可执行代码 。这就是回调函数的定义了 。
回调函数就是一个函数,和其它函数没有任何区别 。
注意,回调函数是一种软件设计上的概念,和某个编程语言没有关系,几乎所有的编程语言都能实现回调函数 。
对于一般的函数来说,我们自己编写的函数会在自己的程序内部调用,也就是说函数的编写方是我们自己,调用方也是我们自己 。
但回调函数不是这样的,虽然函数编写方是我们自己,但是函数调用方不是我们,而是我们引用的其它模块,也就是第三方库,我们调用第三方库中的函数,并把回调函数传递给第三方库,第三方库中的函数调用我们编写的回调函数,如图所示:
文章插图
而之所以需要给第三方库指定回调函数,是因为第三方库的编写者并不清楚在某些特定节点,比如我们举的例子油条制作完成、接收到网络数据、文件读取完成等之后该做什么,这些只有库的使用方才知道,因此第三方库的编写者无法针对具体的实现来写代码,而只能对外提供一个回调函数,库的使用方来实现该函数,第三方库在特定的节点调用该回调函数就可以了 。
另一点值得注意的是,从图中我们可以看出回调函数和我们的主程序位于同一层中,我们只负责编写该回调函数,但并不是我们来调用的 。
最后值得注意的一点就是回调函数被调用的时间节点,回调函数只在某些特定的节点被调用,就像上面说的油条制作完成、接收到网络数据、文件读取完成等,这些都是事件,也就是event,本质上我们编写的回调函数就是用来处理event的,因此从这个角度看回调函数不过就是event handler,因此回调函数天然适用于事件驱动编程event-driven,我们将会在后续文章中再次回到这一主题 。
回调的类型我们已经知道有两种类型的回调,这两种类型的回调区别在于回调函数被调用的时机 。注意,接下来会用到同步和异步的概念 。
同步回调
这种回调就是通常所说的同步回调synchronous callbacks、也有的将其称为阻塞式回调blocking callbacks,或者什么修饰都没有,就是回调,callback,这是我们最为熟悉的回调方式 。
当我们调用某个函数A并以参数的形式传入回调函数后,在A返回之前回调函数会被执行,也就是说我们的主程序会等待回调函数执行完成,这就是所谓的同步回调 。
推荐阅读
- 堆和二叉堆的实现和特性
- 芯片加密技术是什么?如何实现芯片加密
- 用 Lua 操作文件中的数据
- windows域软件下发策略
- 开封|开封招91人!报名开始
- 儿童房装修如何设计
- 东南亚风格装修有哪些特点
- 医患关系紧张的原因的对策
- 比较大众的韩国化妆品牌推荐
- 混搭风格的装修设计说明