范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

计算机内功心法六10张图就让你彻底理解回调函数

  不知你是不是也有这样的疑惑,我们为什么需要回调函数这个概念呢?直接调用函数就可以了?回调函数到底有什么作用?程序员到底该如何理解回调函数?
  这篇文章就来为你解答这些问题, 读完这篇文章后你的武器库将新增一件功能强大的利器 。 一切要从这样的需求说起
  假设你们公司要开发下一代国民App"明日油条",一款主打解决国民早餐问题的App,为了加快开发进度,这款应用由A小组和B小组协同开发。
  其中有一个核心模块由A小组开发然后供B小组调用,这个核心模块被封装成了一个函数,这个函数就叫make_youtiao()。
  如果make_youtiao()这个函数执行的很快并可以立即返回,那么B小组的同学只需要: 调用make_youtiao() 等待该函数执行完成 该函数执行完后继续后续流程
  从程序执行的角度看这个过程是这样的: 保存当前被执行函数的上下文 开始执行make_youtiao()这个函数 make_youtiao()执行完后,控制转回到调用函数中
  1602596924819
  如果世界上所有的函数都像make_youtiao()这么简单,那么程序员大概率就要失业了,还好程序的世界是复杂的,这样程序员才有了存在的价值。现实情况并不容易   现实中make_youtiao()这个函数需要处理的数据非常庞大,假设有10000个,那么make_youtiao(10000)不会立刻返回,而是可能需要10分钟才执行完成并返回。   这时你该怎么办呢?想一想这个问题。   可能有的同学就像把头埋在沙子里的鸵鸟一样:和刚才一样直接调用不可以吗,这样多简单。   是的,这样做没有问题,但就像爱因斯坦说的那样"一切都应该尽可能简单,但是不能过于简单"。   想一想直接调用会有什么问题?   显然直接调用的话,那么调用线程会被阻塞暂停,在等待10分钟后才能继续运行。在这10分钟内该线程不会被操作系统分配CPU,也就是说该线程得不到任何推进。   这并不是一种高效的做法。   没有一个程序员想死盯着屏幕10分钟后才能得到结果。   那么有没有一种更加高效的做法呢?   想一想我们上一篇中那个一直盯着你写代码的老板(见《从小白到高手,你需要理解同步与异步》),我们已经知道了这种一直等待直到另一个任务完成的模式叫做同步。   如果你是老板的话你会什么都不干一直盯着员工写代码吗?因此一种更好的做法是程序员在代码的时候老板该干啥干啥,程序员写完后自然会通知老板,这样老板和程序员都不需要相互等待,这种模式被称为异步。   回到我们的主题,这里一种更好的方式是调用make_youtiao()这个函数后不再等待这个函数执行完成,而是直接返回继续后续流程,这样A小组的程序就可以和make_youtiao()这个函数同时进行了,就像这样:   1602597258523
  在这种情况下,回调(callback)就必须出场了。为什么我们需要回调callback   有的同学可能还没有明白为什么在这种情况下需要回调,别着急,我们慢慢讲。   假设我们"明日油条"App代码第一版是这样写的:make_youtiao(10000); sell();   可以看到这是最简单的写法,意思很简单,制作好油条后卖出去。   1602597572027
  我们已经知道了由于make_youtiao(10000)这个函数10分钟才能返回,你不想一直死盯着屏幕10分钟等待结果,那么一种更好的方法是让make_youtiao()这个函数知道制作完油条后该干什么,即,更好的调用make_youtiao的方式是这样的:"制作10000个油条,炸好后卖出去",因此调用make_youtiao就变出这样了:make_youtiao(10000, sell);   看到了吧,现在make_youtiao这个函数多了一个参数,除了指定制作油条的数量外还可以指定制作好后该干什么,第二个被make_youtiao这个函数调用的函数就叫回调,callback。   现在你应该看出来了吧,虽然sell函数是你定义的,但是这个函数却是被其它模块调用执行的,就像这样:   1602598090503
  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吗?这个问题该怎么解决呢?关于这个问题的答案,你可以参考这里。异步回调   故事到这里还没有结束。   在上面的示例中,虽然我们使用了回调这一概念,也就是调用方实现回调函数然后再将该函数当做参数传递给其它模块调用。   但是,这里依然有一个问题,那就是make_youtiao函数的调用方式依然是同步的,关于同步异步请参考《从小白到高手,你需要理解同步与异步》,也就是说调用方是这样实现的:make_youtiao(10000, sell); // make_youtiao函数返回前什么都做不了   1602598090503
  我们可以看到,调用方必须等待make_youtiao函数返回后才可以继续后续流程,我们再来看下make_youtiao函数的实现:void make_youtiao(int num, func call_back) {     real_make_youtiao(num);     call_back(); //执行回调  }   看到了吧,由于我们要制作10000个油条,make_youtiao函数执行完需要10分钟,也就是说即便我们使用了回调,调用方完全不需要关心制作完油条后的后续流程,但是调用方依然会被阻塞10分钟,这就是同步调用的问题所在。   如果你真的理解了上一节的话应该能想到一种更好的方法了。   没错,那就是异步调用。   关于异步回调,你可以参考这里。新的编程思维模式   让我们再来仔细地看一下这个过程。   程序员最熟悉的思维模式是这样的:调用某个函数,获取结果处理获取到的结果res = request(); handle(res);   这就是函数的同步调用,只有request()函数返回拿到结果后,才能调用handle函数进行处理,request函数返回前我们必须等待,这就是同步调用,其控制流是这样的:   1602683285172
  但是如果我们想更加高效的话,那么就需要异步调用了,我们不去直接调用handle函数,而是作为参数传递给request:request(handle);   我们根本就不关心request什么时候真正的获取的结果,这是request该关心的事情,我们只需要把获取到结果后该怎么处理告诉request就可以了,因此request函数可以立刻返回,真的获取结果的处理可能是在另一个线程、进程、甚至另一台机器上完成。   这就是异步调用,其控制流是这样的:   1602684355493
  从编程思维上看,异步调用和同步有很大的差别,如果我们把处理流程当做一个任务来的话,那么同步下整个任务都是我们来实现的,但是异步情况下任务的处理流程被分为了两部分:第一部分是我们来处理的,也就是调用request之前的部分第二部分不是我们处理的,而是在其它线程、进程、甚至另一个机器上处理的。   我们可以看到由于任务被分成了两部分,第二部分的调用不在我们的掌控范围内,同时只有调用方才知道该做什么,因此在这种情况下回调函数就是一种必要的机制了。   也就是说回调函数的本质就是"只有我们才知道做些什么,但是我们并不清楚什么时候去做这些,只有其它模块才知道,因此我们必须把我们知道的封装成回调函数告诉其它模块"。   现在你应该能看出异步回调这种编程思维模式和同步的差异了吧。   接下来我们给回调一个较为学术的定义正式定义   在计算机科学中,回调函数是指一段以参数的形式传递给其它代码的可执行代码。   这就是回调函数的定义了。   回调函数就是一个函数,和其它函数没有任何区别。   注意,回调函数是一种软件设计上的概念,和某个编程语言没有关系,几乎所有的编程语言都能实现回调函数。   对于一般的函数来说,我们自己编写的函数会在自己的程序内部调用,也就是说函数的编写方是我们自己,调用方也是我们自己。   但回调函数不是这样的,虽然函数编写方是我们自己,但是函数调用方不是我们,而是我们引用的其它模块,也就是第三方库,我们调用第三方库中的函数,并把回调函数传递给第三方库,第三方库中的函数调用我们编写的回调函数,如图所示:   1601686717372
  而之所以需要给第三方库指定回调函数,是因为第三方库的编写者并不清楚在某些特定节点,比如我们举的例子油条制作完成、接收到网络数据、文件读取完成等之后该做什么,这些只有库的使用方才知道,因此第三方库的编写者无法针对具体的实现来写代码,而只能对外提供一个回调函数,库的使用方来实现该函数,第三方库在特定的节点调用该回调函数就可以了。   另一点值得注意的是,从图中我们可以看出回调函数和我们的主程序位于同一层中,我们只负责编写该回调函数,但并不是我们来调用的。   最后值得注意的一点就是回调函数被调用的时间节点,回调函数只在某些特定的节点被调用,就像上面说的油条制作完成、接收到网络数据、文件读取完成等,这些都是事件,也就是event,本质上我们编写的回调函数就是用来处理event的,因此从这个角度看回调函数不过就是event handler,因此回调函数天然适用于事件驱动编程event-driven,我们将会在后续文章中再次回到这一主题。为什么异步回调这种思维模式正变得的越来越重要   在同步模式下,服务调用方会因服务执行而被阻塞暂停执行,这会导致整个线程被阻塞,因此这种编程方式天然不适用于高并发动辄几万几十万的并发连接场景,   针对高并发这一场景,异步其实是更加高效的,原因很简单,你不需要在原地等待,因此从而更好的利用机器资源,而回调函数又是异步下不可或缺的一种机制。回调地狱,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) {                 ....             });         });     }); });   我想不需要再强调什么了吧,你觉得这两种写法哪个更容易理解,代码更容易维护呢?   博主有幸曾经维护过这种类型的代码,不得不说每次增加新功能的时候恨不得自己化为两个分身,一个不得不去重读一边代码;另一个在一旁骂自己为什么当初选择维护这个项目。   异步回调代码稍不留意就会跌到回调陷阱中,那么有没有一种更好的办法既能结合异步回调的高效又能结合同步编码的简单易读呢?   幸运的是,答案是肯定的,关于答案你可以参考这里。总结   在这篇文章中,我们从一个实际的例子出发详细讲解了回调函数这种机制的来龙去脉,这是应对高并发、高性能场景的一种极其重要的编码机制,异步加回调可以充分利用机器资源,实际上异步回调最本质上就是事件驱动编程,这是我们接下来要重点讲解的内容。

李一男造车交卷了!首款车长这样车东西(公众号chedongxi)作者昊晗编辑晓寒李一男造车,交卷了!就在本周末,车东西受邀抢先静态体验了牛创新能源的首款车型自游家NV。这是继官方发布车辆外观图之后,首次向外界公当手机用上PC级SSD是什么飞速感受?拯救者Y90电竞手机测评疫情下催生了更多宅经济,客观上也给近两年越来越火热的电竞产业加了把火,继而催动手机厂商推出外观更酷炫性能更极致体验更趁手的电竞手机。电竞手机如何出圈?我想,除了推出更酷炫的外观捆绑vivoX80Pro渲染图首发6亿像素让苹果汗颜,2K直屏让用户欢呼vivo这些年进步飞快,不仅孵化出了子品牌iQOO,还在旗舰机领域有巨大突破,主打影像旗舰的vivoX系列赢得了市场好评,比如去年的vivoX70Pro,凭借全能焦段蔡司影像和V12022电池铝箔供需缺口加大,值得布局近几年随着新能源汽车产业的高速发展,电池中各产业链需求也在加大,其中就包括电池铝箔子行业。我在工业网看到的数据是从22开始电池铝箔三年的需求缺口是1。0万吨1。3万吨1。7万吨。缺5分钟搞懂分布式基础概念分布式基础概念1微服务拒绝大型单体应用,基于业务边界进行服务微化拆分,各个服务独立部署运行2集群分布式节点集群是一种物理形态将几台服务器集中在一起,实现同一业务分布式是一种工作方式天玑8100官宣!12256仅需2499?跑分超骁龙888,发哥站起来了从骁龙888到骁龙8Gen1,外表性能强劲,但是却不顾散热问题,旗舰机温度测试的多次翻车,降频锁帧的操作,让消费者望而却步。而此前一直被媒体炒得火热的天玑9000旗舰芯片,号称功耗知名爆料人3月是苹果(AAPL。US)推出199美元iPhone的绝佳时机智通财经APP获悉,知名苹果爆料人MarkGurman表示,苹果(AAPL。US)计划在3月推出5G版iPhoneSE,但最大的新闻可能是当前机型的降价。廉价版iPhone有助于苹python查询AD域信息的方法总结很多公司都在使用AD域来管理用户及组织架构,我们可以使用Python来进行自动化运维。网上很多资料不全而且都很古老,特地根据最近的代码编写经历,编写了如下通过python查询AD域golang爬虫,构造http自定义请求11构造1个客户端clienthttp。Client2构造请求methodGETurlhttpsuseragent。xxxxxx。comrequest,errhttp。NewRequ只会vlookup函数已经OUT了,Excel中12种查询用法全在这Excel相信大家用的最多的函数就是vlookup函数,但是多数人对这个函数也只是会简单的查询操作。今天我们就来学习一下Excel函数的12种查询用法。用法1vlookup最简单的小米发布无线充电预研新技术充电功率最高达50W日前,小米手机官微宣布,正式发布小感量磁吸无线充电预研技术,磁吸无线充电功率最高可达50W,损耗降低50。据悉,该技术包括两款小感量磁吸充电产品小米超薄磁吸无线充(30W)和小米主
公知们常挂在嘴边的瑞典发展模式,咋就走不下去了?瑞典在历史上也算是一个强国了,北欧雄狮,只不过现在有人用这个名号更多说的是它的足球。历史上鼎盛时期的瑞典版图不限于现如今的国内区域,周边国家也被他囊括了,甚至势力都不限于北欧了。后动物世界,广州长隆风光在穗多年,第一次去广州长隆野生动物世界,大熊猫,狮子,斑马,老虎,骆驼,大象,长颈鹿,獴哥。公园以大规模野生动物种群放养和自驾车观赏为特色,集动植物的保护研究旅游观赏科普教育为一体动物世界,广州长隆风光在穗多年,第一次去广州长隆野生动物世界,大熊猫,狮子,斑马,老虎,骆驼,大象,长颈鹿,獴哥。公园以大规模野生动物种群放养和自驾车观赏为特色,集动植物的保护研究旅游观赏科普教育为一体东湖冬色,绮丽风光本着休闲的目的,去东湖公园与深圳水库漫步了一圈。由华为Mate40Pro拍摄,部分使用五倍潜望式超长焦。山上的三面菩萨佛像。由华为mate40Pro拍摄于2021年1月2日下午深圳保护海外利益,军队和安保公司如何相辅相成?我们有哪些短板?我们改革开放以后,特别是我们有了一带一路,这意味着确实大量的中国企业,中国公民会走向世界,走出国门,走向这个世界上的各个角落。那这个世界我们知道,其实纷繁复杂,有很多地方是相对发达莫斯科航展和珠海航展相比有哪些不一样的亮点呢?昨天中国珠海航展开幕了,本来是去年举办的但因为新冠疫情所以推迟到了今年,开幕当天一如既往举行了炫技飞行表演。而参展的武器种类更是让人看的目不暇接,用炫目一词更为恰当。那不禁要问了,自己组建了冲锋队,自己又亲手把它清洗掉,希特勒为啥这么做?1934年6月30号希特勒突然用霹雳手段清洗冲锋队,这个事件也被世人称之为迷的一个话题。希特勒是亲手创立所谓冲锋队,在他掌权之后又把它残忍的清洗掉,为什么?这就是所谓的迷,所以我们无论是拿破仑还是希特勒入侵地广人稀的俄罗斯,为何都一败涂地?俄罗斯的历史很多人还是很感兴趣的,毕竟是世界上一个重要很大的一个国家,两个节点,一个节点就是公元1000年古罗斯帝国,其实当时是很落后的,那个相当于中国北宋的时候,我们多发达啊。(今年西瓜为啥普遍都那么贵呢?你实现吃瓜自由了么?很多人说,今年西瓜感觉有点贵是不是?确实有点贵,过去常见的一块钱二块钱的瓜几乎找不到了。真的有时候买一个瓜要花50块钱确实有点舍不得。很多网友就说了那今年大家只能在微博上吃瓜了,去世界第八,韩国潜艇成功试射弹道导弹,军方却为何不敢大方承认?就在本月4号韩国海军张保皋级常规导弹潜艇,首艇岛山安昌浩号成功试射了一枚潜射弹道导弹。韩国媒体称在发射成功后,韩国将成为世界第八个掌握潜射弹道导弹技术的国家。在完成本次潜射导弹试验稀有蓝色车漆涂装1989年法拉利F40展开拍卖F40不仅是车厂为庆祝40周年而推出的车款,更是法拉利创办人EnzoFerrari在一生中所设计出的最后一款跑车,其历史意义与份量是毋庸置疑的,然而除了最为经典的RossoCors