8bi*_*kie 647
由于该死的东西的名称,开发人员常常被回调所困惑.
回调函数是一个函数,它是:
想象一个回调函数如何工作的好方法是它是一个函数,它被传递到函数的" 后面 ".
也许一个更好的名字将是"追逐后"功能.
此构造对于异步行为非常有用,我们希望在上一个事件完成时发生活动.
伪代码:
// A function which accepts another function as an argument
// (and will automatically invoke that function when it completes - note that there is no explicit call to callbackFunction)
funct printANumber(int number, funct callbackFunction) {
printout("The number you provided is: " + number);
}
// a function which we will use in a driver function as a callback function
funct printFinishMessage() {
printout("I have finished printing numbers.");
}
// Driver method
funct event() {
printANumber(6, printFinishMessage);
}
Run Code Online (Sandbox Code Playgroud)
调用event()的结果:
The number you provided is: 6
I have finished printing numbers.
Run Code Online (Sandbox Code Playgroud)
此处输出的顺序很重要.由于之后调用了回调函数,因此最后打印"我已完成打印数字",而不是先打印.
回调是所谓的,因为它们使用指针语言.如果您不使用其中一个,请不要使用名称"回调".只是理解它只是一个名称来描述一个方法,它作为另一个方法的参数提供,这样当调用父方法时(无论条件,如按钮点击,计时器滴答等)及其方法体完成,然后调用回调函数.
有些语言支持支持多个回调函数参数的构造,并根据父函数的完成方式调用(即在父函数成功完成的情况下调用一个回调,在父函数抛出一个函数时调用另一个回调)具体错误等).
Roh*_*jee 216
回调函数是您提供给另一段代码的函数,允许该代码调用它.
你为什么想做这个?假设您需要调用一个服务.如果服务立即返回,您只需:
例如,假设服务是factorial
功能.当你想要的值时5!
,你会调用factorial(5)
,并会发生以下步骤:
您当前的执行位置已保存(在堆栈中,但这并不重要)
执行被移交给 factorial
当factorial
完成时,它会将结果的地方,你可以得到它
执行回到[1]中的位置
现在假设factorial
花了很长时间,因为你给它巨大的数字,它需要在一些超级计算集群上运行.假设您需要5分钟才能返回结果.你可以:
保持你的设计,并在你睡着的时候晚上运行你的程序,这样你就不会在一半时间盯着屏幕
设计程序做其他的事情,而factorial
在做它的事
如果您选择第二个选项,则回调可能对您有用.
为了利用回调模式,您希望能够以factorial
下列方式调用:
factorial(really_big_number, what_to_do_with_the_result)
Run Code Online (Sandbox Code Playgroud)
第二个参数what_to_do_with_the_result
是你发送给它的函数factorial
,希望factorial
在返回之前调用它的结果.
是的,这意味着factorial
需要编写支持回调.
现在假设您希望能够将参数传递给回调.现在你不能,因为你不会打电话给它,factorial
是.因此factorial
需要编写以允许您传入参数,并在调用它时将它们交给您的回调.它可能看起来像这样:
factorial (number, callback, params)
{
result = number! // i can make up operators in my pseudocode
callback (result, params)
}
Run Code Online (Sandbox Code Playgroud)
现在factorial
允许此模式,您的回调可能如下所示:
logIt (number, logger)
{
logger.log(number)
}
Run Code Online (Sandbox Code Playgroud)
你的电话factorial
会是
factorial(42, logIt, logger)
Run Code Online (Sandbox Code Playgroud)
如果你想从中归还什么logIt
怎么办?嗯,你不能,因为factorial
没有注意它.
好吧,为什么不能factorial
只返回你的回调返回的内容?
由于执行意味着在factorial
完成时被移交给回调,所以它实际上不应该向其调用者返回任何内容.理想情况下,它会以某种方式在另一个线程/进程/机器中启动它的工作并立即返回,以便您可以继续,可能是这样的:
factorial(param_1, param_2, ...)
{
new factorial_worker_task(param_1, param_2, ...);
return;
}
Run Code Online (Sandbox Code Playgroud)
现在这是一个"异步调用",这意味着当你调用它时,它会立即返回,但还没有真正完成它的工作.因此,您确实需要机制来检查它,并在完成后获得结果,并且您的程序在此过程中变得更加复杂.
顺便说一句,使用这种模式,factorial_worker_task
可以异步启动回调并立即返回.
答案是保持回调模式.每当你想写
a = f()
g(a)
Run Code Online (Sandbox Code Playgroud)
并且f
将被异步调用,您将改为编写
f(g)
Run Code Online (Sandbox Code Playgroud)
where g
作为回调传递.
这从根本上改变了程序的流程拓扑结构,并且需要一些时间来适应.
通过为您提供即时创建功能的方法,您的编程语言可以为您提供很多帮助.在上面的代码中,函数g
可能小到print (2*a+1)
.如果您的语言要求您将其定义为单独的功能,并且具有完全不必要的名称和签名,那么如果您经常使用此模式,您的生活将会变得令人不愉快.
另一方面,如果你的语言允许你创建lambdas,那么你的形状要好得多.然后你会写出类似的东西
f( func(a) { print(2*a+1); })
Run Code Online (Sandbox Code Playgroud)
哪个好多了
你如何将回调函数传递给factorial
?好吧,你可以通过多种方式实现这一目标.
如果被调用的函数在同一进程中运行,则可以传递一个函数指针
或者您可能希望fn name --> fn ptr
在程序中维护字典,在这种情况下您可以传递名称
也许您的语言允许您就地定义函数,可能作为lambda!在内部,它正在创建某种对象并传递指针,但您不必担心这一点.
也许你正在调用的函数是在一个完全独立的机器上运行,而你使用像HTTP这样的网络协议来调用它.您可以将回调公开为HTTP可调用函数,并传递其URL.
你明白了.
在我们进入的这个网络时代,我们调用的服务通常是通过网络进行的.我们通常对这些服务没有任何控制权,即我们没有对它们进行编写,我们没有对它们进行维护,我们无法确保它们已经启动或者它们的执行方式.
但是,当我们等待这些服务作出响应时,我们不能指望我们的程序会阻塞.意识到这一点,服务提供商通常使用回调模式设计API.
JavaScript非常好地支持回调,例如使用lambdas和闭包.JavaScript世界中有很多活动,无论是在浏览器上还是在服务器上.甚至还有针对移动设备开发的JavaScript平台.
随着我们向前发展,越来越多的人将编写异步代码,这对于这种理解至关重要.
dan*_*nio 91
请注意,回调是一个单词.
维基百科回调页面很好地解释了它.
从维基百科页面引用:
在计算机编程中,回调是对可执行代码或一段可执行代码的引用,它作为参数传递给其他代码.这允许较低级别的软件层调用在较高级别层中定义的子例程(或函数).
Tho*_*att 41
回调函数是在满足某个条件时应该调用的函数.不是立即调用,而是在将来的某个点调用回调函数.
通常,它在启动任务时使用,它将异步完成(即在调用函数返回后将完成一段时间).
例如,请求网页的功能可能要求其调用者提供将在网页下载完成时调用的回调函数.
Zan*_* XY 33
我相信这种"回调"术语在很多地方被错误地使用了.我的定义是这样的:
回调函数是一个函数,您传递给某人并让他们在某个时间点调用它.
我想人们只是阅读维基定义的第一句话:
回调是对可执行代码或一段可执行代码的引用,它作为参数传递给其他代码.
我一直在使用大量的API,看到各种不好的例子.许多人倾向于命名一个函数指针(对可执行代码的引用)或匿名函数(一段可执行代码)"回调",如果它们只是函数,为什么你需要另一个名字?
实际上只有wiki定义中的第二句显示了回调函数和普通函数之间的差异:
这允许较低级别的软件层调用在较高级别层中定义的子例程(或函数).
所以区别在于你要传递函数的人以及传入函数的调用方式.如果您只是定义一个函数并将其传递给另一个函数并直接在该函数体中调用它,请不要将其称为回调函数.定义说你的传入函数将被"低级"函数调用.
我希望人们可以在模棱两可的语境中停止使用这个词,它不能帮助人们更好地理解.
Dej*_*kic 33
根据电话系统最容易描述回叫.功能调用类似于通过电话呼叫某人,向她询问问题,获得答案以及挂断电话; 添加回调更改了类比,以便在向她询问问题后,您还会告诉她您的姓名和号码,以便她可以给您回复答案.
- Paul Jakubik,"C++中的回调实现"
BKS*_*eon 28
让我们保持简单.什么是回拨功能?
通过寓言和比喻的例子
我有一个秘书.我每天都要求她:(i)在邮局放下公司的外发邮件,并在她完成后,做:(ii)我在其中一个便条上为她写的任何任务.
现在,粘滞便笺上的任务是什么?任务每天都在变化.
假设在这个特殊的日子里,我要她打印一些文件.所以我把它写在便利贴上,然后把它和她需要发送的外发邮件一起放在她的桌子上.
综上所述:
回调功能是第二个任务:打印掉那些文件.因为它是在邮件被丢弃之后完成的,并且还因为告诉她打印文档的粘滞便笺与她需要发布的邮件一起提供给她.
现在让我们将其与编程词汇联系起来
这就是全部.而已.我希望能为你清理它 - 如果没有,发表评论,我会尽力澄清.
小智 17
这使得回调听起来像方法结束时的return语句.
我不确定他们是什么.
我认为Callbacks实际上是对函数的调用,因为调用和完成了另一个函数.
我也认为Callbacks是为了解决原始的调用问题,有点"嘿!你要求的那个东西?我已经完成了 - 只是想我会让你知道 - 回到你身边".
小智 17
Call After会比愚蠢的名字,回调更好.当函数内或满足条件时,调用另一个函数,Call After函数,作为参数接收的函数.
不是在函数内硬编码内部函数,而是编写一个函数来接受已经编写的Call After函数作为参数.在通话结束后可能会基于功能接收参数的代码检测状态变化被调用.
Pre*_*raj 17
什么是回调?
什么是回调函数?
一个回调函数是传递给另一个函数的函数(我们称之为其他功能otherFunction
)作为参数,并且回调函数被调用(或执行)内otherFunction
.
function action(x,y,callback){return callback(x,y); }
函数乘法(x,y){return x*y; }
函数加法(x,y){return x + y; }
警报(动作(10,10,乘法)); //输出:100
警报(动作(10,10,加)); //输出:20
在SOA中,回调允许插件模块从容器/环境访问服务.
ale*_*lex 15
回调函数是您为现有函数/方法指定的函数,在操作完成时调用,需要额外处理等.
例如,在Javascript中,或者更具体地说是jQuery,您可以指定在动画完成时调用的回调参数.
在PHP中,该preg_replace_callback()
函数允许您提供在匹配正则表达式时将调用的函数,并将匹配的字符串作为参数传递.
小智 10
看图像:)
主程序调用库函数(也可能是系统级函数)和回调函数名.此回调函数可能以多种方式实现.主程序根据需要选择一个回调.
最后,库函数在执行期间调用回调函数.
假设我们有一个函数sort(int *arraytobesorted,void (*algorithmchosen)(void))
,它可以接受一个函数指针作为它的参数,可以在实现的某个时候使用它sort()
.然后,这里函数指针正在寻址的代码algorithmchosen
被称为回调函数.
并且看到的优势是我们可以选择任何算法:
1. algorithmchosen = bubblesort
2. algorithmchosen = heapsort
3. algorithmchosen = mergesort ...
Run Code Online (Sandbox Code Playgroud)
比如,已经使用原型实现了:
1. `void bubblesort(void)`
2. `void heapsort(void)`
3. `void mergesort(void)` ...
Run Code Online (Sandbox Code Playgroud)
这是用于在面向对象编程中实现多态性的概念