为了账号安全,请及时绑定邮箱和手机立即绑定

什么是回调函数?

/ 猿问

什么是回调函数?

什么是回调函数?

什么是回调函数?



查看完整描述

3 回答

?
潇潇雨雨

开发人员常常对回调是什么感到困惑,因为这个该死的东西的名字。

回调函数是一个函数,它是:

  • 可由另一个函数访问,以及
  • 如果第一个函数完成,则在第一个函数之后调用。

想象回调函数是如何工作的一个很好的方法是,它是一个在后面呼叫“传递给它的函数。

也许一个更好的名字应该是“呼叫后”功能。

这个构造对于异步行为非常有用,在异步行为中,我们希望一个活动在上一个事件完成时发生。

伪码:

// 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);
}

如果调用Event(),则结果:

The number you provided is: 6
I have finished printing numbers.

这里输出的顺序很重要。因为回调函数是在之后调用的,所以“我已经完成了打印编号”是最后打印的,而不是首先打印的。

回调之所以被称为回调,是因为它们与指针语言一起使用.如果您不使用其中之一,就不要费劲地使用“回调”这个名称。只需了解,它只是一个名称,用来描述作为参数提供给另一个方法的方法,因此当调用父方法时(无论什么条件,例如按钮单击、计时器滴答等)及其方法主体完成后,回调函数就会被调用。

有些语言支持多个回调函数参数的构造,并根据父函数如何完成调用(即,在父函数成功完成的事件中调用一个回调,在父函数抛出特定错误的事件中调用另一个回调,等等)。


查看完整回答
反对 回复 2019-06-17
?
萧十郎

不透明定义

回调函数是向另一段代码提供的函数,允许该代码调用它。

人为实例

你为什么要这么做?假设您需要调用一个服务。如果服务立即返回,您只需:

  1. 叫吧
  2. 等待结果
  3. 一旦结果出来就继续

例如,假设服务是factorial功能。当你想要5!,你会调用factorial(5),将采取以下步骤:

  1. 保存当前的执行位置(在堆栈上,但这并不重要)

  2. 执行移交给factorial

  3. 什么时候factorial完成后,它会将结果放到您可以到达的地方。

  4. 死刑又回到了它在[1]中的位置。

现在假设factorial花了很长时间,因为你给了它巨大的数字,它需要运行在某些超级计算机集群的某些地方。假设你期望它用5分钟来返回你的结果。你可以:

  1. 保留你的设计,晚上睡觉时运行你的程序,这样你就不会一半时间盯着屏幕

  2. 设计你的程序来做其他的事情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)..如果您的语言要求您将其定义为一个单独的函数,并带有一个完全不必要的名称和签名,那么如果您经常使用此模式,您的生活将会变得不愉快。

另一方面,如果您的语言允许您创建lambdas,那么您的状态要好得多。最后你会写一些类似的东西

f( func(a) { print(2*a+1); })

那就好多了。

如何传递回调

如何将回调函数传递给factorial?嗯,你可以用多种方式来做。

  1. 如果被调用的函数在同一个进程中运行,则可以传递一个函数指针。

  2. 或者你想要保存一本字典fn name --> fn ptr在您的程序中,在这种情况下,您可以传递名称。

  3. 也许您的语言允许您在本地定义函数,可能是lambda!在内部,它正在创建某种对象并传递一个指针,但是您不必担心这一点。

  4. 您调用的函数可能运行在一台完全独立的机器上,并且使用HTTP之类的网络协议调用它。您可以将回调公开为HTTP可调用函数,并传递其URL。

你知道这个主意。

最近回调的兴起

在我们已经进入的网络时代,我们调用的服务通常是通过网络进行的。我们通常对这些服务没有任何控制,即我们没有编写它们,我们没有维护它们,我们无法确保它们已经运行,或者它们是如何执行的。

但是,当我们等待这些服务响应时,我们不能期望我们的程序被阻塞。意识到这一点,服务提供者通常使用回调模式设计API。

JavaScript很好地支持回调,例如使用lambda和闭包。JavaScript世界中有很多活动,无论是在浏览器上还是在服务器上。甚至还有为移动开发的JavaScript平台。

随着我们的前进,越来越多的人将编写异步代码,对此,这种理解将是必不可少的。


查看完整回答
反对 回复 2019-06-17
?
沧海一幻觉

注意,回调是一个单词。

维基百科回调页面解释得很好。

摘自维基百科网页:

在计算机编程中,回调是对可执行代码的引用,或者是作为参数传递给其他代码的一段可执行代码。这允许较低级别的软件层调用在较高级别层中定义的子例程(或函数)。


查看完整回答
反对 回复 2019-06-17
  • 3 回答
  • 0 关注
  • 139 浏览
我要回答

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信