-
2019-06-21 15:36:09
关于回调函数到底是什么,已经困扰了我很久了~
在知乎上看到几位大神的帖子,才恍然大悟
作者:no.body
链接:https://www.zhihu.com/question/19801131/answer/27459821
来源:知乎作者:常溪玲
链接:https://www.zhihu.com/question/19801131/answer/13005983
来源:知乎首先要明确的一点是,函数也可以作为函数的参数来传递
好了,有了这个概念我们来说明回调函数到底是怎么回事
首先至少要有 3 种类型的函数
-
主函数:相当于整个程序的引擎,调度各个函数按序执行
-
回调函数:一个独立的功能函数,如写文件函数
-
中间函数:一个介于主函数和回调函数之间的函数,登记回调函数,通知主函数,起到一个桥梁的作用
接下来我们一起来看下示例代码:
#!/usr/bin/env python3 # -*- coding: UTF-8 -*- # 回调函数1 def callback1(x): return x * 2 # 回调函数2 def callback2(x): return x ** 2 # 中间函数 def middle(x, func): return 100 + func(x) # 主函数 def main(): x = 1 a = middle(x, callback1) print(a) b = middle(x, callback2) print(b) c = middle(x, lambda x: x + 2) print(c) main()
运行结果:
102 101 103
代码看懂以后我们接下来分析一下代码的逻辑
首先我们在主函数执行过程中需要用到一个功能
x * 2
,而callback1
函数就提供这个功能,我们就把这个函数称之为回调函数
(至于为什么要叫“回调函数”,不能叫别的呢?其实这只是人为规定的一个名字。你也可以叫“极客点儿专属函数”,但是到时候你又会问为什么要叫“极客点儿专属函数”,它特么的总的有个名字吧!所以叫“回调函数”就是王八的屁股:规定!)。这时候我们的
主函数
要调用它,但是有的时候在开发过程中遇到需要写硬盘的操作,这时候我们为了避免程序的阻塞,就需要用到异步 I/O。就是你自己先写着玩儿,爸爸去干别的事情去了,等你完事儿再来通知我。正是因为这种机制所以得有一个登记回调函数
和通知主函数执行完成
的“地方”,这个地方就是中间函数
。有上述内容我们就可以推导出回调函数执行的流程了:
-
主函数需要调用回调函数
-
中间函数登记回调函数
-
触发回调函数事件
-
调用回调函数
-
响应回调事件
回调实际上有两种:
阻塞式回调
和延迟式回调
也可以叫做同步回调
和异步回调
两者的区别在于:
在阻塞式回调里,回调函数的调用一定发生在主函数返回之前
在延迟式回调里,回调函数的调用有可能是在起始函数返回之后
上述示例均为 同步回调,异步需要用到多进程、多线程、协程这些概念,下次有机会再说
最后用一个例子说明一下到底说明是回调函数:
你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。
在这个例子里,你的电话号码就叫
回调函数
,你把电话留给店员就叫登记回调函数
,店里后来有货了叫做触发回调事件
,店员给你打电话叫做调用回调函数
,你到店里去取货叫做响应回调事件
。更多相关内容 -
-
详解C#委托,事件与回调函数
2010-11-04 15:26:00详解C#委托,事件与回调函数 -
6.1 Javascript:事件与回调函数
2016-03-13 22:46:39回调函数调函数,,指一个函数A被作为参数传递给另一个函数B,回调函数A会在函数B内被调用(或执行)。回调函数的本质是一种模式,因此回调函数也被称为回调模式。在这里,函数A被称为回调函数深度理解函数也只不过是...事件
当页面要发生一些事情或做一些事情时,我们称其为事件。事件是网页自带的属性,如click、mousemove、load等。
响应某个事件的函数则称为事件处理程序,或者叫做事件侦听器。回调函数
调函数,,指一个函数A被作为参数传递给另一个函数B,回调函数A会在函数B内被调用(或执行)。回调函数的本质是一种模式,因此回调函数也被称为回调模式。在这里,函数A被称为回调函数
深度理解
函数也只不过是个值,要么返回一个值,要么为空值
所以把它当成一个数据来处理即可例:
另一种声明函数的方式
var showName=function(name){//函数名称即为变量名称 alert("Your name is "+name+", your number is "+outValue()); //在这里,函数主体outValue即是变量值 //采用这种表达方式时,又称为函数字面量 } //当函数主体单独出现而没有名称时,被称为函数字面量
var name=showName;
通过这句代码,可以看出,函数可以像变更般操纵
说白了,函数只是一个值引用到函数主体的变量函数的调用与引用
调用函数:
var value=outValue();//outValue()为调用一个函数(也是函数字面量)
引用函数
var value=showName;//其实,showName是指向一个函数主体的引用事件,回调,与HTML属性
回调函数最常用于处理事件
利用Html属性联结回调函数与事件
<body onload="showName();"> <img src="..." onclick="showImage()"/> </body>
onload是Html标签的属性,它的作用是把showName()函数与onload事件联结
onclick是Html标签的属性,它的作用是把showImage()函数与onclick事件联结利用函数引用,分离Html与Javascript代码
使用函数引用设定回调函数
window.οnclick=showName;
onclick是window对象的属性,showName为函数引用
当onlick事件被触发后,将执行showName引用所指向的函数
这些代码完全可以在Javascript中书写代码,使得Html与Jvavascript代码分离通过函数字面量来传递参数
使用函数字面量作为函数引用,而后从函数字面量内调用相应的回调函数
function helloName(name) { //输出“Hello WeAreZero”的带参函数 alert("Hello "+name); } window.onclick=function(evt)//函数字面量function()作为函数引用指派给onclick事件 { //在这里,函数字面量function(evt)完成了对helloName()的调用 helloName("WeAreZero"); }
在这里,函数字面量是一个没有名字的函数,所以又叫匿名函数
evt参数将事件对象作为参数传递给事件入事件处理器,但在这里没有用到evt参数总结
将事件与函数联结,并实现Html代码与Javascript代码分离,不仅利于代码直观性与维护,而且函数会使你Javascript的本领更上一层楼!
-
Opengl事件及回调函数
2015-06-07 19:54:15Opengl中的事件及事件循环,回调函数的介绍,包括鼠标回调函数和键盘回调函数 -
C++回调函数详解
2015-07-31 13:50:43我们经常在C++设计时通过使用回调函数可以使有些应用(如定时器事件回调处理、用回调函数记录某操作进度等)变得非常方便和符合逻辑,那么它的内在机制如何呢,怎么定义呢?它和其它函数(比如钩子函数)有何不同呢?... -
深入VC回调函数的使用详解
2021-01-20 06:00:44回调函数说白了就是事件响应程序,Windows的每个消息可以理解为一个事件,事件的响应代码要由用户自己来定义。用户定义了事件响应的代码,但还要Windows知道这段代码的位置(要不然Windows就不知道如何去调用,这也... -
ocx中事件函数,调用js中的回调函数
2017-11-05 13:15:16ocx和js的交互调用。 ocx中事件函数,调用js中的回调函数例程。 适用初学者。良心奉献。内有说明 -
PHP回调函数概念与用法实例分析
2021-01-20 01:30:32回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。 其他语言里的回调函数的概念与之相似,只不过各种语言里回调函数的实现机制不一样,通俗的... -
C#委托,事件与回调函数详解
2016-06-15 16:08:24C#委托与事件,回调函数详解.Net编程中最经常用的元素,事件必然是其中之一。无论在ASP.NET还是WINFrom开发中,窗体加载(Load),绘制(Paint),初始化(Init)等等。 “protected void Page_Load(object sender, EventArgs e)”这段代码相信没有人不熟悉的。细心一点一定会发现,非常多的事件方法都是带了“object sender, EventArgs e”这两个参数。这是不是和委托非常相似呢? 一、委托(有些书中也称为委派) 委托是什么呢?这个名字的意思已经赋予了我们想象的空间,你是编程的,你现在正在写一个ASP.NET网页,而JS是你不熟悉的,于是你委托你的一位同事来帮助你完成JS部分。这就是委托,把你所不能做的事情交给其他人去做。而怎么知道是哪个人去做呢?当然是要知道名字!而为了区别名字一样的不同人,因此,需要描述一个特征。 在C#中,委托的作用是这样描述的:委托就像一个函数的指针,在程序运行时可以使用它们来调用不同的函数。这个其实和你委托同事完成 JS代码一样。如果有两位同事可以做这件事情,他们只要做的结果能够满足你的需求(就像一个接口),尽管他们做的过程不一样,并且作出的效果也不一样,但是,能够达到你的要求就可以了。 1、简单的委托 那委托需要承载哪些信息呢?首先,它存储了方法名,还有参数列表(方法签名),以及返回的类型。比如: delegate string/*返回类型*/ ProcessDelegate(int i); 这就是一个委托的定义。蓝色部分是声明委托的关键字,红色部分是返回的类型,而黑色部分是委托的类型名,和一个类名差不多,而()里的就是参数部分。它的意思是,你要使用这个委托来做事情的话,那么,做事情的方法必须满足以下条件: 1、返回类型和委托的返回类型一致,这里是string类型; 2、能且只能有一个参数,并且是int类型。 OK,满足以上两个条件,一切就可以工作了:) 例如: 1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 5 namespace TestApp 6 { 7 /// <summary> 8 /// 委托 9 /// </summary> 10 /// <param name="s1"></param> 11 /// <param name="s2"></param> 12 /// <returns></returns> 13 public delegate string ProcessDelegate(string s1, string s2); 14 15 class Program 16 { 17 static void Main(string[] args) 18 { 19 /* 调用方法 */ 20 ProcessDelegate pd = new ProcessDelegate(new Test().Process); 21 Console.WriteLine(pd("Text1", "Text2")); 22 } 23 } 24 25 public class Test 26 { 27 /// <summary> 28 /// 方法 29 /// </summary> 30 /// <param name="s1"></param> 31 /// <param name="s2"></param> 32 /// <returns></returns> 33 public string Process(string s1,string s2) 34 { 35 return s1 + s2; 36 } 37 } 38 } 输出的结果是: Text1Tex2 2、泛型委托 泛型的委托,就是然参数的类型不确定,例如代码改写为: using System; using System.Collections.Generic; using System.Text; namespace TestApp { /// <summary> /// 委托 /// </summary> /// <param name="s1"></param> /// <param name="s2"></param> /// <returns></returns> public delegate string ProcessDelegate<T,S>(T s1, S s2); class Program { static void Main(string[] args) { /* 调用方法 */ ProcessDelegate<string,int> pd = new ProcessDelegate<string,int>(new Test().Process); Console.WriteLine(pd("Text1", 100)); } } public class Test { /// <summary> /// 方法 /// </summary> /// <param name="s1"></param> /// <param name="s2"></param> /// <returns></returns> public string Process(string s1,int s2) { return s1 + s2; } } } 输出的结果就是: Text1100 泛型的详细内容不属于本文的介绍范围,这里不加多说了。 二、事件 在某件事情发生时,一个对象可以通过事件通知另一个对象。比如,前台完成了前台界面,他通知你,可以把前台和你开发的程序整合了。这就是一个事件。可以看出事件是在一个时间节点去触发另外一件事情,而另外一件事情怎么去做,他不会关心。就事件来说,关键点就是什么时候,让谁去做。 在C#中,时间定义关键字是event。例如: event ProcessDelegate ProcessEvent; 整个事件定义方法以及执行过程: using System; using System.Collections.Generic; using System.Text; namespace TestApp { /// <summary> /// 委托 /// </summary> /// <param name="s1"></param> /// <param name="s2"></param> /// <returns></returns> public delegate void ProcessDelegate(object sender, EventArgs e); class Program { static void Main(string[] args) { /* 第一步执行 */ Test t = new Test(); /* 关联事件方法,相当于寻找到了委托人 */ t.ProcessEvent += new ProcessDelegate(t_ProcessEvent); /* 进入Process方法 */ Console.WriteLine(t.Process()); Console.Read(); } static void t_ProcessEvent(object sender, EventArgs e) { Test t = (Test)sender; t.Text1 = "Hello"; t.Text2 = "World"; } } public class Test { private string s1; public string Text1 { get { return s1; } set { s1 = value; } } private string s2; public string Text2 { get { return s2; } set { s2 = value; } } public event ProcessDelegate ProcessEvent; void ProcessAction(object sender, EventArgs e) { if (ProcessEvent == null) ProcessEvent += new ProcessDelegate(t_ProcessEvent); ProcessEvent(sender, e); } //如果没有自己指定关联方法,将会调用该方法抛出错误 void t_ProcessEvent(object sender, EventArgs e) { throw new Exception("The method or operation is not implemented."); } void OnProcess() { ProcessAction(this, EventArgs.Empty); } public string Process() { OnProcess(); return s1 + s2; } } } 感觉到了什么?是不是和代码注入了差不多,相当于是可以用任意符合委托接口(委托确实很像接口)的代码,注入到Process过程。在他返回之前给他赋值。 三、回调函数 回调函数就是把一个方法的传给另外一个方法去执行。在C#有很多回调函数,比如异步操作的时候。这里先举个例子: using System; using System.Collections.Generic; using System.Text; namespace TestApp { /// <summary> /// 委托 /// </summary> /// <param name="s1"></param> /// <param name="s2"></param> /// <returns></returns> public delegate string ProcessDelegate(string s1, string s2); class Program { static void Main(string[] args) { /* 调用方法 */ Test t = new Test(); string r1 = t.Process("Text1", "Text2", new ProcessDelegate(t.Process1)); string r2 = t.Process("Text1", "Text2", new ProcessDelegate(t.Process2)); string r3 = t.Process("Text1", "Text2", new ProcessDelegate(t.Process3)); Console.WriteLine(r1); Console.WriteLine(r2); Console.WriteLine(r3); } } public class Test { public string Process(string s1,string s2,ProcessDelegate process) { return process(s1, s2); } public string Process1(string s1, string s2) { return s1 + s2; } public string Process2(string s1, string s2) { return s1 + Environment.NewLine + s2; } public string Process3(string s1, string s2) { return s2 + s1; } } } 输出结果: Text1Text2 Text1 Text2 Text2Text1 Process方法调用了一个回调函数,当然这里只执行了回调函数。可以看出,可以把任意一个符合这个委托的方法传递进去,意思就是说这部分代码是可变的。而设计上有一个抽离出可变部分代码的原则,这种用法无疑可以用到那种场合了。
-
回调函数 – 灵活的函数指针
2021-01-03 16:53:02回调函数一 同步回调及代码 什么是回调函数? 编程除了分为面向对象和面向过程外,还可以分为系统编程和应用编程。如下图所示,主函数和callback函数在应用层,library函数在系统层。在使用时,main将callback函数... -
opencv 之 鼠标事件,回调函数
2018-04-17 09:54:08主要介绍 鼠标事件的函数原型及参数 回调函数的原型及参数 鼠标事件画矩形的例子指定鼠标操作消息回调函数的函数为SetMouseCallback函数。函数原型: void SetMouseCallback(const string & winname,...主要介绍
鼠标事件的函数原型及参数
回调函数的原型及参数
鼠标事件画矩形的例子
指定鼠标操作消息回调函数的函数为SetMouseCallback函数。
函数原型:
void SetMouseCallback(const string & winname,MouseCallback onMouse,void* userdata=0)
参数:
第一个参数为窗口的名字,
第二个参数用来指定窗口每次鼠标时候发生的时候,被调用函数指针,
第三个参数则为用户定义的传递到回调函数的参数。
其中函数指针原型:
void Foo(int event ,int x ,int y ,int flags ,void * param)。
参数:
event 是鼠标响应类型,CV_EVENT_*变量之一:
EVENT_MOUSEMOVE滑动
EVENT_LBUTTONDOWN 左击
EVENT_RBUTTONDOWN 右击
EVENT_MBUTTONDOWN中键点击
EVENT_LBUTTONUP 左键放开
EVENT_RBUTTONUP 右键放开
EVENT_LBUTTONDBLCLK左键双击
EVENT_RBUTTONDBLCLK 右键双击
EVENT_MBUTTONDBLCLK 中键双击
x和y是鼠标指针在图像坐标系的坐标(不是窗口坐标系)。
flags是CV_EVENT_FLAG的组合,flag的状态有:
EVENT_FLAG_LBUTTON 左键拖拽
EVENT_FLAG_RBUTTON 右键拖拽
EVENT_FLAG_MBUTTON 中键拖拽
EVENT_FLAG_CTRLKEY 按住Ctrl不放
EVENT_FLAG_SHIFTKEY 按住Shift不放
EVENT_FLAG_ALTKEY 按住Alt不放
param是用户定义的传递到setMouseCallback函数调用的参数。
通过鼠标回调函数绘制矩形代码如下:
void MouseEvent(); //主函数
void on_MouseHandle(int event,int x, int y, int flag, void* param); //回调函数void DrawRectangle(Mat& img,Rect box); //画矩形
/*****************鼠标操作*****************/ Rect g_rect; bool g_DrawFlag = false; RNG g_rng(12345); void MouseEvent() { //准备参数 g_rect = Rect(-1, -1, 0, 0); Mat srcImage(600, 600, CV_8UC3), tempImage; srcImage.copyTo(tempImage); g_rect = Rect(-1, -1, 0, 0); srcImage = Scalar::all(0); //设置鼠标操作回调函数 namedWindow("Win"); setMouseCallback("Win", on_MouseHandle, (void*)&srcImage); //绘画 while (1) { srcImage.copyTo(tempImage); if (g_DrawFlag) { DrawRectangle(tempImage, g_rect); } imshow("Win", tempImage); if (waitKey(10) == 27) break;//ESC 退出 } } //鼠标回调事件 void on_MouseHandle(int event, int x, int y, int flag, void* param) { Mat& image = *(Mat*)param; switch (event) { case EVENT_MOUSEMOVE://移动 if (g_DrawFlag) { //计算,g_rect宽高=鼠标当前位置坐标-g_rect左上角的坐标 g_rect.width = x - g_rect.x; g_rect.height = y - g_rect.y; } break; case EVENT_LBUTTONDOWN://左键按下 g_DrawFlag = true; //设置g_rect的初始值在同一个点 g_rect = Rect(x, y, 0, 0); break; case EVENT_LBUTTONUP://左键抬起 g_DrawFlag = false; //当g_rect宽高小于0 //起始点xy坐标置为较小靠左上角的点 //宽高取绝对值 if (g_rect.width < 0) { g_rect.x += g_rect.width; g_rect.width *= -1; } if (g_rect.height < 0) { g_rect.y += g_rect.height; g_rect.height *= -1; } //画矩形 DrawRectangle(image, g_rect); break; } } //矩形绘制函数 void DrawRectangle(Mat& img, Rect box) { //rectangle画矩形 //tl左上角的点,br右下角的点 //Scalar设置颜色,设置为3通道 //g_rng.uniform(0, 255)随机颜色 rectangle(img, box.tl(), box.br(), Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255))); }
效果:
-
C语言 —— 回调函数
2021-07-07 14:56:33void String(void* param) { char* str; str = (char*)param; printf("String:%s\n",str); } 该函数的形参表示可以接收任何类型指针 void *可以接受任何类型的指针 -
JS事件的绑定,回调函数
2020-03-02 21:42:33<script type="text/javascript"> window.onload=function(){ var btn=document.getElementById("btn"); btn.onclick=function(){ /*addEventListenner() -通过这个方法可以为事件...2.回调函数,当事件触发... -
C++类与回调函数
2020-09-20 20:12:33C++类与回调函数 -
阻塞式回调函数和延迟式回调函数
2020-04-30 18:16:57首先,有三种函数: 起始函数:大致可以等同于主函数 中间函数:中间函数把回调函数作为参数传递执行 ...在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做 触... -
C语言中函数指针和回调函数的详解
2019-05-18 23:10:01函数指针:指向函数的指针变量。 因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。如前所述,C在编译时,每一个函数都有一个... -
js 点击事件回调函数传参
2017-09-14 16:44:08点击事件回调函数传参 使用匿名函数 function testFun(event, str) { console.log(str); } var test = document.getElementById('test'); test.addEventListener('click', function(event) { testFun(event... -
Delphi 回调函数及线程使用回调函数
2019-02-25 10:18:58原因:在写线程时,用beginThread函数和creatthread函数可以实现函数调用及被调用函数的参数传递(用...在用线程类的时候,用回调函数的方法时,没法传递被调用函数参数,故写此文。下文自己的demo,测试传递readCo... -
C# 回调函数详解
2021-03-10 10:46:13回调和回调函数是什么 软件模块之间总是存在着一定的接口,回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口。 对于一般的结构化语言,可以通过回调函数来实现回调。回调函数是一个函数... -
Java实现的回调函数
2021-02-12 21:25:16一个回调函数的例子,首先是我为了完成在工厂工作的任务,但是我不能一直在工厂工作,我必须知道工作完以后,就要下班回家。所以做了一个回调函数,通知我,今天工作做完了,可以下班回家啦。具体可以看代码的实现... -
C# Csharp 调用 C++的DLL中的回调函数
2014-03-21 13:50:40一个是C++的DLL以及源码 一个是调用他的C#源码 都是VS2010编译测试通过 -
回调函数详解
2018-12-04 19:26:53一、什么是回调函数 回调函数是指 使用者自己定义一个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。函数是你... -
钩子函数与回调函数的理解
2018-08-08 21:30:04钩子函数与回调函数都是事件处理函数 2.钩子函数是指windows的消息处理机制下,捕获消息的时候立即执行 3.回调函数并不能参与消息处理的过程,所以,它是在消息捕获结束后才执行的函数 举个例子吧: 钩子... -
matlab串口收发及回调函数编程
2011-08-20 16:38:27matlab创建com对象。并采用中断方式响应回调函数,功能完整,包括创建初始化及关闭销毁。回调函数的格式,实现方法。 -
理解事件回调函数、钩子函数
2017-08-04 08:45:01理解事件回调函数、钩子函数 -
C语言 回调函数原理及实现
2019-08-30 18:32:38最近需要实现处理AWSIOT传来的消息回调函数。作为库编程,在老司机的指导下发现不能直接把AWS IOT的回调接口暴露到上层而是应该自己封装回调函数以供上层调用,这样可以更好地解耦合,上层即不需要了解下层的细节。... -
回调函数详解(从根本上理解消息与事件)
2009-04-24 14:41:27word文档,回调函数的详细讲解,清楚明了,解开回调函数的迷雾。并从根本上理解消息与事件的原理。 -
C语言回调函数详解(全网最全)
2021-10-31 00:10:41什么是回调函数2 为什么要用回调函数?3 怎么使用回调函数?4.下面是一个四则运算的简单回调函数例子:5. 回调函数实例(很有用) 一、函数指针 在讲回调函数之前,我们需要了解函数指针。 我们都知道,C语言的灵魂... -
彻底理解回调函数,事件,流
2018-03-13 15:14:51回调,是非常基本的概念,尤其在现今NodeJS诞生与蓬勃发展中变得更加被人们重视。很多朋友学NodeJS,学很久一直摸不着门道,觉得最后在用Express写Web程序,有这样的感觉只能说明没有学懂NodeJS,本质上说不理解回调... -
回调函数是异步吗?回调函数和异步操作的关系
2021-06-18 01:24:05定义:回调函数被认为是一种高级函数,一种被作为参数传递给另一个函数(在这称作"otherFunction")的高级函数,回调函数会在otherFunction内被调用(或执行)。回调函数的本质是一种模式(一种解决常见问题的模式),因此...