什么是回调函数?


当前回答

不透明定义

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

有争议的例子

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

叫它吧等待结果结果出来后继续

例如,假设服务是阶乘函数。当您希望值为5!,您将调用factorial(5),并执行以下步骤:

当前执行位置已保存(在堆栈上,但这并不重要)执行移交给阶乘当阶乘完成时,它会将结果放在你可以得到的地方执行恢复到[1]中的位置

现在假设阶乘花费了很长时间,因为你给了它巨大的数字,它需要在一些超级计算集群上运行。假设您预计需要5分钟才能返回结果。您可以:

保持你的设计并在晚上睡觉时运行你的程序,这样你就不会一半时间盯着屏幕设计你的程序,让它在factorial做它的时候做其他的事情

如果您选择第二个选项,则回调可能对您有效。

端到端设计

为了利用回调模式,您需要能够以以下方式调用factorial:

factorial(really_big_number, what_to_do_with_the_result)

第二个参数what_to_do_with_The_result是一个随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(42, logIt, logger)

如果你想从logIt中返回什么呢?你不能,因为阶乘没有注意到它。

那么,为什么阶乘不能只返回回调返回的值?

使其无阻塞

由于执行是在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名称-->fn ptr的字典,在这种情况下,您可以传递名称也许您的语言允许您就地定义函数,可能是lambda!在内部,它创建某种对象并传递指针,但您不必担心这一点。也许您正在调用的函数是在一台完全独立的机器上运行的,并且您正在使用HTTP之类的网络协议来调用它。您可以将回调公开为HTTP可调用函数,并传递其URL。

你明白了。

最近出现的回调

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

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

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

随着我们的前进,我们中越来越多的人将编写异步代码,对此理解至关重要。

其他回答

看看图片:)

主程序使用回调函数名调用库函数(也可能是系统级函数)。此回调函数可以以多种方式实现。主程序根据需要选择一个回调。

最后,库函数在执行期间调用回调函数。

回调与回调函数

回调是在另一个函数完成执行后执行的函数 — 因此得名“回电”。

什么是回调函数?

将Funs(即函数对象)作为参数或返回Funs的函数称为高阶函数。任何作为参数传递的函数都称为回调函数。回调函数是作为参数传递给另一个函数(让我们调用这个其他函数otherFunction)的函数,回调函数在otherFunction内部调用(或执行)。

    function action(x, y, callback) {
        return callback(x, y);
    }
    
    function multiplication(x, y) {
        return x * y;
    }
    
    function addition(x, y) {
        return x + y;
    }

    alert(action(10, 10, multiplication)); // output: 100

    alert(action(10, 10, addition)); // output: 20

在SOA中,回调允许插件模块从容器/环境访问服务。

来源

回调函数是在满足特定条件时应调用的函数。回调函数不是立即调用,而是在将来的某个时间点调用。

通常,当一个任务正在启动时,它将异步完成(即在调用函数返回后一段时间完成)。

例如,请求网页的函数可能要求其调用方提供回调函数,该回调函数将在网页完成下载时调用。

回调函数是您指定给现有函数/方法的函数,在操作完成时调用,需要额外处理等。

例如,在Javascript中,或者更具体地说,在jQuery中,可以指定在动画完成时调用的回调参数。

在PHP中,preg_replace_callback()函数允许您提供一个在正则表达式匹配时调用的函数,将匹配的字符串作为参数传递。

这个问题的简单答案是回调函数是通过函数指针调用的函数。如果您将一个函数的指针(地址)作为参数传递给另一个函数,则当该指针用于调用它所指向的函数时,称为进行了回调