返回顶部
首页 > 资讯 > 前端开发 > JavaScript >编程中的回调函数有什么作用
  • 278
分享到

编程中的回调函数有什么作用

2024-04-02 19:04:59 278人浏览 安东尼
摘要

本篇内容主要讲解“编程中的回调函数有什么作用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“编程中的回调函数有什么作用”吧!不知你是不是也有这样的疑惑,我们为什么

本篇内容主要讲解“编程中的回调函数有什么作用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“编程中的回调函数有什么作用”吧!

不知你是不是也有这样的疑惑,我们为什么需要回调函数这个概念呢?直接调用函数不就可以了?回调函数到底有什么作用?程序员到底该如何理解回调函数?

一切要从这样的需求说起

假设你们公司要开发下一代国民App“明日油条”,一款主打解决国民早餐问题的App,为了加快开发进度,这款应用由A小组和B小组协同开发。

其中有一个核心模块由A小组开发然后供B小组调用,这个核心模块被封装成了一个函数,这个函数就叫make_youtiao()。

如果make_youtiao()这个函数执行的很快并可以立即返回,那么B小组的同学只需要:

  • 调用make_youtiao()

  • 等待该函数执行完成

  • 该函数执行完后继续后续流程

从程序执行的角度看这个过程是这样的:

  • 保存当前被执行函数的上下文

  • 开始执行make_youtiao()这个函数

  • make_youtiao()执行完后,控制转回到调用函数中

编程中的回调函数有什么作用

如果世界上所有的函数都像make_youtiao()这么简单,那么程序员大概率就要失业了,还好程序的世界是复杂的,这样程序员才有了存在的价值。

现实并不容易

现实中make_youtiao()这个函数需要处理的数据非常庞大,假设有10000个,那么make_youtiao(10000)不会立刻返回,而是可能需要10分钟才执行完成并返回。

这时你该怎么办呢?想一想这个问题。

可能有的同学会问,和刚才一样直接调用不可以吗,这样多简单。

是的,这样做没有问题,但就像爱因斯坦说的那样“一切都应该尽可能简单,但是不能过于简单”。

想一想直接调用会有什么问题?

显然直接调用的话,那么调用线程会被阻塞暂停,在等待10分钟后才能继续运行。在这10分钟内该线程不会被操作系统分配CPU,也就是说该线程得不到任何推进。

这并不是一种高效的做法。没有一个程序员想死盯着屏幕10分钟后才能得到结果。那么有没有一种更加高效的做法呢?

这种一直等待直到另一个任务完成的模式叫做同步。如果你是老板的话你会什么都不干一直盯着员工写代码吗?因此一种更好的做法是程序员在代码的时候老板该干啥干啥,程序员写完后自然会通知老板,这样老板和程序员都不需要相互等待,这种模式被称为异步。

回到我们的主题,这里一种更好的方式是调用make_youtiao()这个函数后不再等待这个函数执行完成,而是直接返回继续后续流程,这样A小组的程序就可以和make_youtiao()这个函数同时进行了,就像这样:

编程中的回调函数有什么作用

在这种情况下,回调(callback)就必须出场了。

为什么我们需要回调callback

有的同学可能还没有明白为什么在这种情况下需要回调,别着急,我们慢慢讲。假设我们“明日油条”App代码第一版是这样写的:

make_youtiao(10000);  sell();

可以看到这是最简单的写法,意思很简单,制作好油条后卖出去。

编程中的回调函数有什么作用

我们已经知道了由于make_youtiao(10000)这个函数10分钟才能返回,你不想一直死盯着屏幕10分钟等待结果,那么一种更好的方法是让make_youtiao()这个函数知道制作完油条后该干什么,即,更好的调用make_youtiao的方式是这样的:“制作10000个油条,炸好后卖出去”,因此调用make_youtiao就变出这样了:

make_youtiao(10000, sell);

看到了吧,现在make_youtiao这个函数多了一个参数,除了指定制作油条的数量外还可以指定制作好后该干什么,第二个被make_youtiao这个函数调用的函数就叫回调,callback。

现在你应该看出来了吧,虽然sell函数是你定义的,但是这个函数却是被其它模块调用执行的,就像这样:

编程中的回调函数有什么作用

make_youtiao这个函数是怎么实现的呢,很简单:

void make_youtiao(int num, func call_back) {  // 制作油条  call_back(); //执行回调  }

这样你就不用死盯着屏幕了,因为你把make_youtiao这个函数执行完后该做的任务交代给make_youtiao这个函数了,该函数制作完油条后知道该干些什么,这样就解放了你的程序。

有的同学可能还是有疑问,为什么编写make_youtiao这个小组不直接定义sell函数然后调用呢?

不要忘了明日油条这个App是由A小组和B小组同时开发的,A小组在编写make_youtiao时怎么知道B小组要怎么用这个模块,假设A小组真的自己定义sell函数就会这样写:

void make_youtiao(int num) {  real_make_youtiao(num);  sell(); //执行回调  }

同时A小组设计的模块非常好用,这时C小组也想用这个模块,然而C小组的需求是制作完油条后放到仓库而不是不是直接卖掉,要满足这一需求那么A小组该怎么写呢?

void make_youtiao(int num) {  real_make_youtiao(num);    if (Team_B) {  sell(); // 执行回调  } else if (Team_D) {  store(); // 放到仓库  } }

故事还没完,假设这时D小组又想使用呢,难道还要接着添加if  else吗?这样的话A小组的同学只需要维护make_youtiao这个函数就能做到工作量饱满了,显然这是一种非常糟糕的设计。

所以你会看到,制作完油条后接下来该做什么不是实现make_youtiao的A小组该关心的事情,很明显只有调用make_youtiao这个函数的使用方才知道。

因此make_youtiao的A小组完全可以通过回调函数将接下来该干什么交给调用方实现,A小组的同学只需要针对回调函数这一抽象概念进行编程就好了,这样调用方在制作完油条后不管是卖掉、放到库存还是自己吃掉等等想做什么都可以,A小组的make_youtiao函数根本不用做任何改动,因为A小组是针对回调函数这一抽象概念来编程的。

以上就是回调函数的作用,当然这也是针对抽象而不是具体实现进行编程这一思想的威力所在。面向对象中的多态本质上就是让你用来针对抽象而不是针对实现来编程的。

异步回调

故事到这里还没有结束。

在上面的示例中,虽然我们使用了回调这一概念,也就是调用方实现回调函数然后再将该函数当做参数传递给其它模块调用。

但是,这里依然有一个问题,那就是make_youtiao函数的调用方式依然是同步的,也就是说调用方是这样实现的:

make_youtiao(10000, sell); // make_youtiao函数返回前什么都做不了

编程中的回调函数有什么作用

我们可以看到,调用方必须等待make_youtiao函数返回后才可以继续后续流程,我们再来看下make_youtiao函数的实现:

void make_youtiao(int num, func call_back) {  real_make_youtiao(num);  call_back(); //执行回调  }

看到了吧,由于我们要制作10000个油条,make_youtiao函数执行完需要10分钟,也就是说即便我们使用了回调,调用方完全不需要关心制作完油条后的后续流程,但是调用方依然会被阻塞10分钟,这就是同步调用的问题所在。

如果你真的理解了上一节的话应该能想到一种更好的方法了。

没错,那就是异步调用。

反正制作完油条后的后续流程并不是调用方该关心的,也就是说调用方并不关心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); // 立刻返回 // 执行后续流程

这时调用方的后续流程可以和制作油条同时进行,这就是函数的异步调用,当然这也是异步的高效之处。

新的编程思维模式

让我们再来仔细的看一下这个过程。程序员最熟悉的思维模式是这样的:

  • 调用某个函数,获取结果

  • 处理获取到的结果

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返回之前回调函数会被执行,也就是说我们的主程序会等待回调函数执行完成,这就是所谓的同步回调。

编程中的回调函数有什么作用

有同步回调就有异步回调。

异步回调

不同于同步回调,  当我们调用某个函数A并以参数的形式传入回调函数后,A函数会立刻返回,也就是说函数A并不会阻塞我们的主程序,一段时间后回调函数开始被执行,此时我们的主程序可能在忙其它任务,回调函数的执行和我们主程序的运行同时进行。

既然我们的主程序和回调函数的执行可以同时发生,因此一般情况下,主程序和回调函数的执行位于不同的线程或者进程中。

编程中的回调函数有什么作用

这就是所谓的异步回调,asynchronous callbacks,也有的资料将其称为deferred callbacks  ,名字很形象,延迟回调。

从上面这两张图中我们也可以看到,异步回调要比同步回调更能充分的利用机器资源,原因就在于在同步模式下主程序会“偷懒”,因为调用其它函数被阻塞而暂停运行,但是异步调用不存在这个问题,主程序会一直运行下去。

因此,异步回调更常见于I/O操作,天然适用于WEB服务这种高并发场景。

回调对应的编程思维模式

让我们用简单的几句话来总结一下回调下与常规编程思维模式的不同。

假设我们想处理某项任务,这项任务需要依赖某项服务S,我们可以将任务的处理分为两部分,调用服务S前的部分PA,和调用服务S后的部分PB。

在常规模式下,PA和PB都是服务调用方来执行的,也就是我们自己来执行PA部分,等待服务S返回后再执行PB部分。

但在回调这种方式下就不一样了。

在这种情况下,我们自己来执行PA部分,然后告诉服务S:“等你完成服务后执行PB部分”。

因此我们可以看到,现在一项任务是由不同的模块来协作完成的。

即:

  • 常规模式:调用完S服务后后我去执行X任务,

  • 回调模式:调用完S服务后你接着再去执行X任务,

其中X是服务调用方制定的,区别在于谁来执行。

为什么异步回调越来越重要

在同步模式下,服务调用方会因服务执行而被阻塞暂停执行,这会导致整个线程被阻塞,因此这种编程方式天然不适用于高并发动辄几万几十万的并发连接场景,

针对高并发这一场景,异步其实是更加高效的,原因很简单,你不需要在原地等待,因此从而更好的利用机器资源,而回调函数又是异步下不可或缺的一种机制。

回调地狱,callback hell

有的同学可能认为有了异步回调这种机制应付起一切高并发场景就可以高枕无忧了。

实际上在计算机科学中还没有任何一种可以横扫一切包治百病的技术,现在没有,在可预见的将来也不会有,一切都是妥协的结果。

那么异步回调这种机制有什么问题呢?

实际上我们已经看到了,异步回调这种机制和程序员最熟悉的同步模式不一样,在可理解性上比不过同步,而如果业务逻辑相对复杂,比如我们处理某项任务时不止需要调用一项服务,而是几项甚至十几项,如果这些服务调用都采用异步回调的方式来处理的话,那么很有可能我们就陷入回调地狱中。

举个例子,假设处理某项任务我们需要调用四个服务,每一个服务都需要依赖上一个服务的结果,如果用同步方式来实现的话可能是这样的:

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) {  ....  });  });  }); });

我想不需要再强调什么了吧,你觉得这两种写法哪个更容易理解,代码更容易维护呢?

有幸曾经维护过这种类型的代码,不得不说每次增加新功能的时候恨不得自己化为两个分身,一个不得不去重读一边代码;另一个在一旁骂自己为什么当初选择维护这个项目

异步回调代码稍不留意就会跌到回调陷阱中,那么有没有一种更好的办法既能结合异步回调的高效又能结合同步编码的简单易读呢?

答案是肯定的,后续再详细讲解这一技术。

到此,相信大家对“编程中的回调函数有什么作用”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

--结束END--

本文标题: 编程中的回调函数有什么作用

本文链接: https://lsjlt.com/news/66903.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 编程中的回调函数有什么作用
    本篇内容主要讲解“编程中的回调函数有什么作用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“编程中的回调函数有什么作用”吧!不知你是不是也有这样的疑惑,我们为什么...
    99+
    2024-04-02
  • Java中回调函数的作用是什么
    这期内容当中小编将会给大家带来有关Java中回调函数的作用是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。先定义一个接口,规定回答问题的条件是打我手机public interface&nbs...
    99+
    2023-06-17
  • Keras中回调函数的作用是什么
    Keras中的回调函数是一种用于在训练过程中监控模型性能、调整模型参数以及实现自定义功能的机制。回调函数可以在训练过程中的不同阶段触...
    99+
    2024-04-02
  • golang函数中回调函数的作用
    go 中回调函数在函数中作为参数传递,用于在特定事件或条件发生后执行特定动作,增强代码的可重用性和扩展性。主要作用为:事件处理:作为事件侦听器的回调处理程序,在事件发生时采取动作。数据处...
    99+
    2024-04-25
    golang 回调函数 git
  • javascript回调函数有什么用
    这篇文章主要为大家展示了“javascript回调函数有什么用”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“javascript回调函数有什么用”这篇文章吧。 ...
    99+
    2024-04-02
  • Golang 函数返回值在并发编程中的作用是什么?
    go 函数返回值在并发编程中扮演着关键角色:使 goroutine 之间通过通信通道进行数据交换成为可能。允许函数返回并发任务的结果,以便主程序可以处理结果。控制并发执行的流程,例如等待...
    99+
    2024-04-13
    返回值 并发编程 golang
  • js中什么是回调函数
    小编给大家分享一下js中什么是回调函数,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!什么是回调函数?回调函数有什么缺点回调函数是...
    99+
    2024-04-02
  • python中什么是回调函数
    python中的回调函数是指一下几种回调函数是指通过函数参数传递到其它代码的,某一块可执行代码的引用。回调函数就是通过函数名调用的函数,如把函数的名字作为参数传递给另一个函数,当这个参数被用来调用其所指向的函数时,这个函数就是回调函数。回调...
    99+
    2024-04-02
  • php中什么是回调函数
    小编给大家分享一下php中什么是回调函数,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!什么是回调函数:回调函数,或简称回调,是指通过函数参数传递到其它代码的,某一...
    99+
    2023-06-15
  • jquery中什么是回调函数
    本教程操作环境:windows7系统、jquery3.6.1版本、Dell G3电脑。函数也是对象想弄明白回调函数,首先的清楚地明白函数的规则。在javascript中,函数是比较奇怪的,但它确确实实是对象。确切地说,函数是用Functio...
    99+
    2022-11-22
    jquery javascript
  • c语言中回调函数的使用方法及作用是什么
    本篇内容介绍了“c语言中回调函数的使用方法及作用是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!目录前言一、通过这节课程你能掌握以下知识...
    99+
    2023-06-20
  • python中返回函数的作用是什么
    这期内容当中小编将会给大家带来有关python中返回函数的作用是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。python可以做什么Python是一种编程语言,内置了许多有效的工具,Python几乎无...
    99+
    2023-06-14
  • jquery中ajax回调函数是什么
    这篇文章将为大家详细讲解有关jquery中ajax回调函数是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。 回调函数有:1、beforeS...
    99+
    2024-04-02
  • Node.js中的回调函数怎么用
    这篇文章主要介绍了Node.js中的回调函数怎么用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Node.js中的回调函数怎么用文章都会有所收获,下面我们一起来看看吧。同样,在Node.js中,比如I/O操作发...
    99+
    2023-06-27
  • JavaScript的回调函数是什么
    这篇文章主要介绍“JavaScript的回调函数是什么”,在日常操作中,相信很多人在JavaScript的回调函数是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java...
    99+
    2024-04-02
  • JavaScript中的回调函数是什么及如何用
    今天小编给大家分享一下JavaScript中的回调函数是什么及如何用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1.什么是...
    99+
    2023-07-04
  • js中回调函数怎么用
    这篇文章主要为大家展示了“js中回调函数怎么用”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“js中回调函数怎么用”这篇文章吧。JS 回调函数详解JS回调函数何为...
    99+
    2024-04-02
  • python中的函数有什么作用
    本篇内容介绍了“python中的函数有什么作用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1、函数的价值主要体现在调用时,而非定义时。调用...
    99+
    2023-06-30
  • python回调函数是什么
    python回调函数是指将一个函数作为参数传递给另一个函数,并在特定事件发生时由另一个函数调用的函数。回调函数通常用于异步编程、事件驱动编程和处理大量数据时的回调机制。其应用场景如下:1、事件处理;2、异步编程;3、迭代器和生成器。本教程操...
    99+
    2023-12-11
    python 回调函数
  • JavaScript 中回调函数有哪些
    这期内容当中小编将会给大家带来有关JavaScript 中回调函数有哪些,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。什么是函数函数是在其中有一组代码的逻辑构件,用来执行...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作