什么是回调函数?
什么是回调函数?
开发人员常常因为该死的东西的名称而被回调所困惑。
回调函数是一个函数,它是:
一旦它的父函数完成,然后调用作为参数传递的函数。
伪代码:
// The callback method
function meaningOfLife() {
log("The meaning of life is: 42");
}
// A method which accepts a callback method as an argument
// takes a function reference to be executed when printANumber completes
function printANumber(int number, function callbackFunction) {
print("The number you provided is: " + number);
}
// Driver method
function event() {
printANumber(6, meaningOfLife);
}
如果你调用event(),结果如下:
The number you provided is: 6
The meaning of life is: 42
由于使用指针语言,回调是所谓的。 如果您不使用其中的一种,请不要在名称的“回拨”上工作。 只要理解它只是一个名字来描述一个作为另一个方法的参数提供的方法,这样当父方法被调用(无论什么条件,比如按钮点击,计时器tick等),并且它的方法体完成时,然后调用回调方法,换句话说就是在另一个函数的“后面”调用。
不透明的定义
回调函数是您提供给另一段代码的函数,允许它被该代码调用。
受挫的例子
你为什么想做这个? 假设有一项服务需要调用。 如果服务立即返回,您只需:
例如,假设服务是factorial
函数。 当你想要5!
的价值5!
,你会调用factorial(5)
,并且会发生以下步骤:
您当前的执行位置已保存(在堆栈中,但这不重要)
执行交给factorial
当factorial
完成时,它会将结果放在您可以达到的地方
执行回到原来的位置[1]
现在假设factorial
需要很长时间,因为你给它的数量很大,它需要在某个超级计算集群上运行。 假设您预计需要5分钟才能返回结果。 你可以:
保持你的设计,并在晚上睡觉时运行你的程序,这样你就不会在一半时间内盯着屏幕
设计你的程序来做其他事情,而factorial
正在做它的事情
如果您选择第二个选项,那么回调可能适用于您。
端到端设计
为了利用回调模式,你想要的是能够以下面的方式调用factorial
:
factorial(really_big_number, what_to_do_with_the_result)
第二个参数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)
}
现在, factorial
允许这种模式,您的回调可能如下所示:
logIt (number, logger)
{
logger.log(number)
}
而你的要求是factorial
factorial(42, logIt, logger)
如果你想从logIt
返回一些东西呢? 那么,你不能,因为factorial
没有关注它。
那么,为什么不能factorial
回报你的回调回报?
使其非阻塞
由于执行是为了在factorial
完成时移交给回调,所以它确实不应该向其调用者返回任何东西。 理想情况下,它会以某种方式在另一个线程/进程/机器中启动它的工作,并立即返回,以便可以继续,也许如下所示:
factorial(param_1, param_2, ...)
{
new factorial_worker_task(param_1, param_2, ...);
return;
}
这现在是一个“异步调用”,这意味着当你调用它时,它立即返回,但还没有真正完成它的工作。 所以你需要一些机制来检查它,并在结束时得到结果,并且程序在这个过程中变得更加复杂。
顺便说一下,使用这种模式factorial_worker_task
可以异步启动您的回调并立即返回。
所以你会怎么做?
答案是保持在回调模式中。 每当你想写的时候
a = f()
g(a)
而f
将被异步调用,您将改为写入
f(g)
其中g
作为回调传递。
这从根本上改变了程序的流程拓扑,并且需要一些习惯。
您的编程语言可以为您提供一种即时创建功能的方法,从而为您提供很多帮助。 在上面的代码中,函数g
可能小于print (2*a+1)
。 如果你的语言要求你将其定义为一个单独的函数,并且完全不必要的名字和签名,那么如果你使用这种模式很多,你的生活将会变得不愉快。
另一方面,如果你的语言允许你创建lambda表达式,那么你的状态会更好。 然后你会写出类似的东西
f( func(a) { print(2*a+1); })
这太好了。
如何传递回调
你如何将回调函数传递给factorial
? 那么,你可以通过很多方式来做到这一点。
如果被调用函数在同一个进程中运行,则可以传递一个函数指针
或者,也许你想在你的程序中维护一个fn name --> fn ptr
的字典,在这种情况下你可以通过这个名字
也许你的语言允许你就地定义函数,可能是lambda! 它在内部创建某种对象并传递指针,但您不必担心这一点。
也许你所调用的函数在一个完全独立的机器上运行,并且你正在使用像HTTP这样的网络协议来调用它。 您可以将您的回调公开为HTTP可调用函数,并传递其URL。
你明白了。
回调近期兴起
在这个我们进入的网络时代,我们所调用的服务通常通过网络。 我们经常无法控制这些服务,例如我们没有编写它们,我们没有对它们进行维护,我们无法确保它们已经启动,或者它们的表现如何。
但是我们不能指望我们的程序在我们等待这些服务作出响应时被阻止。 意识到这一点,服务提供商通常使用回调模式来设计API。
JavaScript支持很好的回调,例如lambdas和闭包。 JavaScript世界中有很多活动,无论是在浏览器上还是在服务器上。 甚至还有针对手机开发的JavaScript平台。
随着我们前进,越来越多的人将编写异步代码,对此,这种理解是必不可少的。
请注意,回调是一个字。
维基百科回调页面很好地解释了它。
从维基百科页面引用:
在计算机编程中,回调是对可执行代码或可执行代码的引用,它作为参数传递给其他代码。 这允许较低级别的软件层调用在较高级别层中定义的子例程(或函数)。
链接地址: http://www.djcxy.com/p/5783.html