什么是回调函数?

What is a callback function?

提问人: 提问时间:5/5/2009 最后编辑:Cheekysoft 更新时间:7/10/2023 访问量:464438

问:

什么是回调函数?

与语言无关的 回调

评论

13赞 Fakher 6/23/2015
你可以在这里找到: stackoverflow.com/a/9652434/3343174 关于回调的最佳解释
3赞 Brian Tompsett - 汤莱恩 7/8/2015
如何用通俗易懂的英语解释回调的可能重复?它们与从一个函数调用另一个函数有何不同?
5赞 sss 6/16/2019
我发现的对回调的最好解释 youtube.com/watch?v=xHneyv38Jro
7赞 mins 6/28/2020
维基百科上有一个不简单的解释。然而,我们可以更简单地说:使用回拨原则就像把名片交给某人并告诉:如果你需要我,给我回电话,号码在卡片上。在编程中,一个函数将自身的引用留给另一段代码,例如通过注册,另一个代码在适当的时候使用此引用来调用(回调)函数,例如当某些事件发生时。在这种情况下,回调也被命名为事件处理程序

答:

115赞 danio 5/5/2009 #1

维基百科上的回调页面很好地解释了这一点:

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

评论

4赞 Anonymous 5/5/2009
这也以不同的方式导致了答案。名词“回调”是被“回调”的东西,就像经过关闭的东西被关闭,用来登录的东西是登录一样。
2赞 Thomas 2/24/2015
维基百科实际上有一些非常棒的编程内容。我一直觉得“回电”一词最好用“我要回电......”这句话来解释。
0赞 MarcoZen 4/23/2015
javascriptissexy.com/ 的精彩解释......;我将在这里重新发布;回调函数是作为参数传递给另一个函数的函数,回调函数在 otherFunction 内调用或执行。请注意,click 方法参数中的项是一个函数,而不是一个变量。该项目是一个回调函数 $(“#btn_1”).click(function() { alert(“Btn 1 Clicked”);正如您在前面的示例中看到的,我们将一个函数作为参数传递给 click 方法以使其执行 –
0赞 danio 6/7/2016
@CodyBugstein我认为这个问题很简单,维基百科页面在第一段中解释了这个概念,几乎不需要更长的答案。例如,这里最好的答案只是用不同的词说了与维基百科第一段相同的想法,对我来说,伪代码没有显示维基百科示例所做的任何事情。 干燥 ;-)
0赞 alain.janinm 12/19/2018
这很有趣,因为这个答案引用了维基百科,其中引用了 8bitjunkie 的 Stack Overflow 答案
18赞 alex 5/5/2009 #2

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

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

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

2赞 Chintan Parikh 5/5/2009 #3

一个重要的使用领域是将其中一个函数注册为句柄(即回调),然后发送消息/调用某个函数来执行某些工作或处理。现在处理完成后,被调用的函数将调用我们注册的函数(即现在回调完成),从而表明我们处理完成。
这个维基百科链接以图形方式很好地解释了。

45赞 Thomas Bratt 5/6/2009 #4

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

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

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

评论

0赞 Ojonugwa Jude Ochalifu 7/25/2014
在您的第一句话中,您说但我认为回调是在父函数完成执行并且不依赖于条件 (?) 时调用的。"...when a condition is met"
0赞 Thomas Bratt 7/26/2014
“特定条件”只是意味着他们通常是出于某种原因被召唤的,而不是随机的。当父级/创建者仍在执行时,可以调用回调 - 如果程序员没有预料到,这可能会导致争用条件。
21赞 Mauritico 10/28/2010 #5

这使得回调听起来像方法末尾的 return 语句。

我不确定它们是什么。

我认为回调实际上是对一个函数的调用,这是调用另一个函数并完成的结果。

我还认为回调是为了解决原始调用,以一种“嘿!你要的那个东西?我已经做到了——只是想让你知道——回到你身边”。

评论

1赞 8bitjunkie 9/26/2011
+1 用于质疑回调与返回语句。我曾经被这个困扰,许多与我一起工作的毕业生也是如此。
7赞 Zain Ali 3/10/2011 #6

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

53赞 NateH 9/16/2011 #7

外行的回答是,它是一个函数,它不是由你调用的,而是由用户或浏览器在某个事件发生后或处理完某些代码后调用的。

817赞 8bitjunkie 9/26/2011 #8

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

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

  • 可通过其他功能访问,以及
  • 如果第一个函数完成,则在第一个函数之后调用

想象回调函数如何工作的一个很好的方法是,它是一个在它被传递到的函数的后面“调用”的函数。

也许更好的名称是“调用后”函数。

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

伪代码:

// 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.

此处输出的顺序很重要。由于回调函数是在之后调用的,因此“我已完成打印数字”是最后打印的,而不是第一个打印的。

回调之所以被称为回调,是因为它们与指针语言一起使用。如果您不使用其中之一,请不要为“callback”这个名字而苦恼。只需了解它只是一个名称,用于描述作为参数提供给另一个方法的方法,这样,当调用父方法(任何条件,例如按钮单击、计时器滴答等)并且其方法主体完成时,就会调用回调函数。

某些语言支持支持多个回调函数参数的构造,并根据父函数的完成方式进行调用(即,在父函数成功完成的情况下调用一个回调,在父函数抛出特定错误时调用另一个回调,等等)。

评论

35赞 CodyBugstein 4/11/2013
你的例子很好,但我不明白为什么术语是“回调”。meaningOfLife什么时候被“召回”?
5赞 Max Yari 4/27/2015
你好,关于.因此,如果函数作为参数传递给另一个函数,但从父函数运行时的中间调用,那么它不被视为函数?once its parent method completes, the function which this argument represents is then calledparent(cb) {dostuff1(); cb(); dostuff2()}callback
3赞 Kamran Bigdely 9/14/2016
@MaxYari:恕我直言,它仍然被视为回调。这里重要的是父函数将以某种方式使用输入函数(又名回调)。它可以在中间或结束时调用,也可以在满足条件时调用。
14赞 BenKoshy 11/29/2016
@8bitjunkie谢谢 - 但是在 printANumber 函数中调用的 meaningOfLife 方法在哪里?
9赞 Jordan 5/18/2018
这根本不是真的:“在第一个函数完成后自动调用”。回调根本不需要执行,更不用说自动执行了。事实上,回调在父函数完成之前完成的情况并不少见。我非常不喜欢人们将回调描述为“稍后”执行的函数。对于正在了解它们的人来说,这是非常令人困惑的。简单地说,回调只是作为参数传递给其他函数的函数。时期。更好的解释包括解释函数引用的 WHY 回调。
12赞 user1103138 12/23/2011 #9

看图片:)this is how it works

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

最后,库函数在执行过程中调用回调函数。

评论

7赞 Tim Post 12/23/2011
你介意也添加一个文字解释吗?如果图像消失,则此答案将丢失所有上下文。
0赞 12/23/2011
其他人的文字最好地解释了这一点。我唯一觉得缺少的是图像:)
0赞 DiBosco 4/30/2019
在我在这里看到的所有冗长的描述中,这个是让我去“啊,现在我看到它的用途了。投赞成票。
0赞 XouDo 8/23/2021
好吧,它不一定是库函数才能以这种方式回调你。您的程序可以创建一个单独的线程来执行某些任务并触发回调机制,而无需外部库。
35赞 Zane XY 7/20/2012 #10

我相信这个“回调”术语在很多地方都被错误地使用了。我的定义是这样的:

回调函数是您传递给某人并让 他们在某个时间点称之为。

我认为人们只是阅读了 wiki 定义的第一句话:

回调是对可执行代码的引用,或者是对一段 可执行代码,作为参数传递给其他代码。

我一直在使用很多 API,看到各种不好的例子。许多人倾向于将函数指针(对可执行代码的引用)或匿名函数(一段可执行代码)命名为“callback”,如果它们只是函数,为什么还需要另一个名称呢?

实际上,wiki 定义中只有第二句话揭示了回调函数和普通函数之间的区别:

这允许较低级别的软件层调用子例程(或 function) 在更高级别的层中定义。

因此,区别在于您将向谁传递函数以及如何调用传入的函数。如果只是定义一个函数并将其传递给另一个函数并直接在该函数体中调用它,则不要将其称为回调。该定义表示传入的函数将由“较低级别”函数调用。

我希望人们能停止在模棱两可的上下文中使用这个词,它不能帮助人们理解更好,只会更糟。

评论

2赞 CodyBugstein 4/11/2013
你的回答很有道理......但我很难想象它。你能举个例子吗?
3赞 Viku 1/23/2014
@Zane Wong :: 在最后你写的“定义说你传入的函数将被”低级“函数调用。您能解释一下低级函数表示什么吗?如果你举个例子会更好.
0赞 Yousuf Azad 7/24/2016
举个例子就好了
1赞 XouDo 12/12/2019
我认为经典函数调用和回调风格之间的区别在于依赖方向:如果模块 A 依赖于(“使用”)模块 B,则 A 调用 B 的函数,它就不是回调。如果 A 将对其函数的引用传递给 B,则 B 调用 A 的函数,这是一个回调:与模块依赖项相比,调用向后。
0赞 Abdel Aleem 1/4/2021
@ZaneXY先生说:“如果你只是定义一个函数并将其传递给另一个函数,并直接在该函数体中调用它,请不要将其称为回调。这难道不是同步回调的定义吗,即立即执行的回调?
7赞 Neeraj 5/4/2013 #11

假设我们有一个函数,它可以接受一个函数指针作为它的参数,可以在实现中的某个时刻使用。然后,这里函数指针要寻址的代码被称为回调函数sort(int *arraytobesorted,void (*algorithmchosen)(void))sort()algorithmchosen

看到优点是我们可以选择任何算法,例如:

  1.    algorithmchosen = bubblesort
  2.    algorithmchosen = heapsort
  3.    algorithmchosen = mergesort   ...

比如说,已经用原型实现了:

  1.   `void bubblesort(void)`
  2.   `void heapsort(void)`
  3.   `void mergesort(void)`   ...

这是在面向对象编程中实现多态性的概念

评论

0赞 MarcoZen 4/23/2015
javascriptissexy.com/ 的精彩解释......;我将在这里重新发布;回调函数是作为参数传递给另一个函数的函数,回调函数在 otherFunction 内调用或执行。请注意,click 方法参数中的项是一个函数,而不是一个变量。该项目是一个回调函数 $(“#btn_1”).click(function() { alert(“Btn 1 Clicked”);正如您在前面的示例中看到的,我们将一个函数作为参数传递给 click 方法以使其执行 –
18赞 Smack MacDougal 11/19/2013 #12

Call After 会比愚蠢的名字 callback 更好。当或如果条件在函数中得到满足时,请调用另一个函数,即作为参数接收的函数 Call After 函数。

与其在函数中对内部函数进行硬编码,不如编写一个函数来接受已经编写的 Call After 函数作为参数。Call After 可能会根据接收参数的函数中的代码检测到的状态更改进行调用。

评论

0赞 8bitjunkie 5/13/2016
这是个好主意。我选择了“在后面打电话”来试图解释这一点。我可以看到像马丁·福勒(Martin Fowler)这样的人在他的开创性博客上将“呼叫后”作为这些的新术语来推广。
0赞 sheriffderek 6/23/2023
如果它根本没有名字怎么办?这难道不是最不令人困惑的吗?它只是一个函数。
44赞 DejanLekic 1/11/2014 #13

回拨最容易用电话系统来描述。函数呼叫类似于打电话给某人,问她一个问题,得到答案,然后挂断电话;添加回调会改变类比,以便在问她一个问题后,你还会给她你的姓名和号码,这样她就可以给你回电并给出答案。

-- Paul Jakubik,“C++中的回调实现”

评论

1赞 Koray Tugay 3/23/2017
所以我的名字和号码是一个函数?
0赞 gherson 3/18/2019
不,如果“回拨”是一个好名字,那将是一个类比:你要求电话接线员打电话。结束。
0赞 Abdel Aleem 1/4/2021
我从这个可爱的答案中推断出,“回调”的行为是回调函数终止并返回父函数时。我错了吗?
257赞 Rohit Chatterjee 5/4/2014 #14

不透明定义

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

人为的例子

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

  1. 称呼它
  2. 等待结果
  3. 结果出来后继续

例如,假设服务是函数。当您需要 的值时,将调用 ,并将执行以下步骤:factorial5!factorial(5)

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

  2. 执行移交给factorial

  3. 完成后,它会将结果放在您可以到达的地方factorial

  4. 执行回到原来的位置 [1]

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

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

  2. 将程序设计为在执行其操作的同时执行其他操作factorial

如果您选择第二个选项,则回调可能适合您。

端到端设计

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

factorial(really_big_number, what_to_do_with_the_result)

第二个参数 , 是您发送到的函数,希望在返回之前根据其结果调用它。what_to_do_with_the_resultfactorialfactorial

是的,这意味着需要编写阶乘来支持回调。

现在假设您希望能够将参数传递给回调。现在你不能,因为你不会叫它,是。所以需要编写以允许您传入参数,并且当它调用它时,它只会将它们交给您的回调。它可能看起来像这样:factorialfactorial

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)

如果你想从中退回一些东西怎么办?好吧,你不能,因为没有注意它。logItfactorial

那么,为什么不能只返回回调返回的内容呢?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)

where 作为回调传递。g

这从根本上改变了程序的流拓扑结构,并且需要一些时间来适应。

你的编程语言可以为你提供一种即时创建函数的方法,从而对你有很大帮助。在上面的代码中,该函数可能小到 .如果你的语言要求你将其定义为一个单独的函数,使用完全不必要的名称和签名,那么如果你经常使用这种模式,你的生活会变得不愉快。gprint (2*a+1)

另一方面,如果你的语言允许你创建lambda,那么你的状态就会好得多。然后你最终会写出类似的东西

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 平台。

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

评论

0赞 Daniel Viglione 8/7/2016
是的,我了解 lambas 如何在 javascript 和 ruby 中工作。和 java 8,但旧版本的 java 没有使用 lambas,而是使用类,我想知道这种回调是如何工作的。仍然是一个优于另一个的答案。
0赞 mikemaccana 3/26/2019
不。每个作为函数的参数都不是回调。它可以是一个迭代器函数,或者一个排序函数,或者许多其他东西。请参阅术语“回调”的词源。
0赞 drlolly 4/23/2020
是否可以公平地说,这种回调的使用是“远程过程调用”的一种形式?
1赞 chirag20388 4/27/2015 #15

回调函数(也称为高阶函数)是作为参数传递给另一个函数的函数,回调函数在父函数内调用(或执行)。

$("#button_1").click(function() {
  alert("button 1 Clicked");
});

在这里,我们将一个函数作为参数传递给 click 方法。click 方法将调用(或执行)我们传递给它的回调函数。

评论

2赞 danio 3/2/2016
回调函数本身不是高阶函数。它被传递给更高阶的函数。
31赞 Premraj 5/11/2015 #16

回调与回调函数

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

什么是回调函数

  • 将 Funs(即函数对象)作为参数或返回 Funs 的函数称为高阶函数。
  • 作为参数传递的任何函数都称为回调函数。
  • 回调函数是作为参数传递给另一个函数(我们称之为另一个函数)的函数,并且该回调函数在 .otherFunctionotherFunction
    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 中,callback 允许插件模块从容器/环境中访问服务。

评论

2赞 danio 3/2/2016
回调函数本身不是高阶函数。它被传递给更高阶的函数。
54赞 BenKoshy 2/29/2016 #17

类比的简单解释

每天,我都去上班。老板告诉我:

哦,当你完成这些后,我有一个额外的任务给你:

伟大。他递给我一张纸条,上面写着一个任务——这个任务是一个回调函数。它可以是任何东西:

ben.doWork( and_when_finished_wash_my_car)

明天可能是:

ben.doWork( and_tell_me_how_great_i_am)

关键是回电必须在我完成工作完成!您最好阅读上面其他答案中包含的代码,我无需重复。

4赞 somenath mukhopadhyay 5/16/2017 #18

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

在 C 语言中使用函数指针进行回调

在 C 语言中,回调是使用函数指针实现的。函数指针 - 顾名思义,是指向函数的指针。

例如,int (*ptrFunc) ();

这里,ptrFunc 是一个指向函数的指针,该函数不带任何参数并返回一个整数。不要忘记输入括号,否则编译器会假定 ptrFunc 是一个普通的函数名称,它不取任何内容并返回指向整数的指针。

下面是一些代码来演示函数指针。

#include<stdio.h>
int func(int, int);
int main(void)
{
    int result1,result2;
    /* declaring a pointer to a function which takes
       two int arguments and returns an integer as result */
    int (*ptrFunc)(int,int);

    /* assigning ptrFunc to func's address */                    
    ptrFunc=func;

    /* calling func() through explicit dereference */
    result1 = (*ptrFunc)(10,20);

    /* calling func() through implicit dereference */        
    result2 = ptrFunc(10,20);            
    printf("result1 = %d result2 = %d\n",result1,result2);
    return 0;
}

int func(int x, int y)
{
    return x+y;
}

现在让我们尝试使用函数指针来理解 C 语言中回调的概念。

完整的程序有三个文件:callback.c、reg_callback.h 和 reg_callback.c。

/* callback.c */
#include<stdio.h>
#include"reg_callback.h"

/* callback function definition goes here */
void my_callback(void)
{
    printf("inside my_callback\n");
}

int main(void)
{
    /* initialize function pointer to
    my_callback */
    callback ptr_my_callback=my_callback;                        
    printf("This is a program demonstrating function callback\n");
    /* register our callback function */
    register_callback(ptr_my_callback);                          
    printf("back inside main program\n");
    return 0;
}

/* reg_callback.h */
typedef void (*callback)(void);
void register_callback(callback ptr_reg_callback);


/* reg_callback.c */
#include<stdio.h>
#include"reg_callback.h"

/* registration goes here */
void register_callback(callback ptr_reg_callback)
{
    printf("inside register_callback\n");
    /* calling our callback function my_callback */
    (*ptr_reg_callback)();                               
}

如果我们运行这个程序,输出将是

这是一个演示函数回调的程序 内部register_callback 内部my_callback 回到主程序内部

上层函数调用下层函数作为正常调用,回调机制允许下层函数通过指向回调函数的指针调用上层函数。

Java 中使用接口回调

Java 没有函数指针的概念 它通过其接口机制实现回调机制 在这里,我们声明了一个接口,而不是一个函数指针,该接口具有一个方法,当被调用方完成其任务时将被调用

让我通过一个例子来演示它:

回调接口

public interface Callback
{
    public void notify(Result result);
}

调用方或高级类

public Class Caller implements Callback
{
Callee ce = new Callee(this); //pass self to the callee

//Other functionality
//Call the Asynctask
ce.doAsynctask();

public void notify(Result result){
//Got the result after the callee has finished the task
//Can do whatever i want with the result
}
}

被叫方或下层函数

public Class Callee {
Callback cb;
Callee(Callback cb){
this.cb = cb;
}

doAsynctask(){
//do the long running task
//get the result
cb.notify(result);//after the task is completed, notify the caller
}
}

使用 EventListener 模式的回调

  • 列表项

此模式用于通知 0 到 n 个观察者/侦听器特定任务已完成

  • 列表项

Callback 机制和 EventListener/Observer 机制的区别在于,在回调中,被调用方通知单个调用方,而在 Eventlisener/Observer 中,被调用方可以通知任何对该事件感兴趣的人(通知可能会转到应用程序的其他一些尚未触发任务的部分)

让我通过一个例子来解释一下。

事件接口

public interface Events {

public void clickEvent();
public void longClickEvent();
}

类小部件

package com.som_itsolutions.training.java.exampleeventlistener;

import java.util.ArrayList;
import java.util.Iterator;

public class Widget implements Events{

    ArrayList<OnClickEventListener> mClickEventListener = new ArrayList<OnClickEventListener>(); 
    ArrayList<OnLongClickEventListener> mLongClickEventListener = new ArrayList<OnLongClickEventListener>();

    @Override
    public void clickEvent() {
        // TODO Auto-generated method stub
        Iterator<OnClickEventListener> it = mClickEventListener.iterator();
                while(it.hasNext()){
                    OnClickEventListener li = it.next();
                    li.onClick(this);
                }   
    }
    @Override
    public void longClickEvent() {
        // TODO Auto-generated method stub
        Iterator<OnLongClickEventListener> it = mLongClickEventListener.iterator();
        while(it.hasNext()){
            OnLongClickEventListener li = it.next();
            li.onLongClick(this);
        }

    }

    public interface OnClickEventListener
    {
        public void onClick (Widget source);
    }

    public interface OnLongClickEventListener
    {
        public void onLongClick (Widget source);
    }

    public void setOnClickEventListner(OnClickEventListener li){
        mClickEventListener.add(li);
    }
    public void setOnLongClickEventListner(OnLongClickEventListener li){
        mLongClickEventListener.add(li);
    }
}

类按钮

public class Button extends Widget{
private String mButtonText;
public Button (){
} 
public String getButtonText() {
return mButtonText;
}
public void setButtonText(String buttonText) {
this.mButtonText = buttonText;
}
}

“类”复选框

public class CheckBox extends Widget{
private boolean checked;
public CheckBox() {
checked = false;
}
public boolean isChecked(){
return (checked == true);
}
public void setCheck(boolean checked){
this.checked = checked;
}
}

活动类

软件包 com.som_itsolutions.training.java.exampleeventlistener;

public class Activity implements Widget.OnClickEventListener
{
    public Button mButton;
    public CheckBox mCheckBox;
    private static Activity mActivityHandler;
    public static Activity getActivityHandle(){
        return mActivityHandler;
    }
    public Activity ()
    {
        mActivityHandler = this;
        mButton = new Button();
        mButton.setOnClickEventListner(this);
        mCheckBox = new CheckBox();
        mCheckBox.setOnClickEventListner(this);
        } 
    public void onClick (Widget source)
    {
        if(source == mButton){
            mButton.setButtonText("Thank you for clicking me...");
            System.out.println(((Button) mButton).getButtonText());
        }
        if(source == mCheckBox){
            if(mCheckBox.isChecked()==false){
                mCheckBox.setCheck(true);
                System.out.println("The checkbox is checked...");
            }
            else{
                mCheckBox.setCheck(false);
                System.out.println("The checkbox is not checked...");
            }       
        }
    }
    public void doSomeWork(Widget source){
        source.clickEvent();
    }   
}

其他类

public class OtherClass implements Widget.OnClickEventListener{
Button mButton;
public OtherClass(){
mButton = Activity.getActivityHandle().mButton;
mButton.setOnClickEventListner(this);//interested in the click event                        //of the button
}
@Override
public void onClick(Widget source) {
if(source == mButton){
System.out.println("Other Class has also received the event notification...");
}
}

主类

public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Activity a = new Activity();
OtherClass o = new OtherClass();
a.doSomeWork(a.mButton);
a.doSomeWork(a.mCheckBox);
}
}

从上面的代码中可以看出,我们有一个称为 events 的接口,它基本上列出了我们的应用程序可能发生的所有事件。Widget 类是所有 UI 组件(如 Button、Checkbox)的基类。这些 UI 组件是从框架代码实际接收事件的对象。Widget 类实现了 Events 接口,并且它有两个嵌套接口,即 OnClickEventListener 和 OnLongClickEventListener

这两个接口负责侦听 Widget 派生的 UI 组件(如 Button 或 Checkbox)上可能发生的事件。因此,如果我们将此示例与之前使用 Java 接口的 Callback 示例进行比较,这两个接口将用作 Callback 接口。因此,更高级别的代码(Here Activity)实现了这两个接口。每当小部件发生事件时,都会调用更高级别的代码(或在更高级别的代码中实现的这些接口的方法,这里是 Activity)。

现在让我讨论一下 Callback 和 Eventlistener 模式之间的基本区别。正如我们已经提到的,使用 Callback,被叫方只能通知一个调用方。但是,对于 EventListener 模式,应用程序的任何其他部分或类都可以注册 Button 或 Checkbox 上可能发生的事件。此类的示例是 OtherClass。如果看到 OtherClass 的代码,就会发现它已将自身注册为 ClickEvent 的侦听器,该侦听器可能发生在 Activity 中定义的 Button 中。有趣的是,除了 Activity(调用方)之外,每当 Button 上发生单击事件时,也会通知此 OtherClass。

评论

0赞 Quentin 6/27/2017
避免仅链接答案。“勉强超过外部网站的链接”的答案可能会被删除
1赞 Garvin 1/8/2018 #19

回调函数作为参数传递给另一个函数的函数。

function test_function(){       
 alert("Hello world");  
} 

setTimeout(test_function, 2000);

注意:在上面的示例中test_function用作 setTimeout 函数的参数。

评论

1赞 dferenc 1/8/2018
欢迎来到 Stack Overflow!在回答问题之前,请务必阅读现有答案。这个答案已经提供了。与其重复答案,不如对现有答案进行投票。可以在这里找到一些写好答案的指南。
0赞 XouDo 1/31/2022
嗯,大多数时候都是这样,但有时,你通过设置对象实例的属性来传递回调函数。例如:(在 C# 中)myButton.Click += (sender, args) => SomeFunction();
3赞 XouDo 5/24/2018 #20

回调函数是将(作为引用或指针)传递给某个函数或对象的函数。 此函数或对象将在以后的任何时间(可能多次)出于任何目的调用此函数:

  • 通知任务结束
  • 请求两个项目之间的比较(如在 C qsort() 中)
  • 报告流程的进度
  • 通知事件
  • 委派对象的实例化
  • 委派区域的绘制

...

因此,将回调描述为在另一个函数或任务结束时调用的函数过于简单(即使这是一个常见的用例)。

4赞 Yoji Yamamoto 4/26/2019 #21

回调是将一个函数作为参数传递给另一个函数,并在进程完成后调用该函数的想法。

如果你通过上面的精彩答案得到了回调的概念,我建议你应该了解它的想法的背景。

“是什么让他们(计算机科学家)开发回调?” 您可能会发现一个问题,即阻塞。(尤其是阻塞 UI) 回调并不是唯一的解决方案。 还有很多其他的解决方案(例如:线程、期货、承诺......

2赞 TheFuzzyGiggler 9/26/2022 #22

我在这个答案上晚了 13 年,但在自己学会之后,我想我会在这里放另一个答案,以防有人像我一样感到困惑。

其他答案总结了“什么是回调”这个问题的关键。

它只是一个函数,在完成某些事情时调用另一个函数。

让我印象深刻的是一些例子,“你现在做了这个,现在做那个。 就像为什么可以这样使用它,当我可以自己调用方法或函数时?

所以这里有一个快速的、真实世界的例子,希望它能为某人“点击”。

Ultra 伪代码

首先,您将遇到的核心问题......

Multithreaded Method(Some arguments)
  {
    Do fancy multithreaded stuff....
  }

Main()
 {
   Some stuff I wanna do = some tasks
   Multhreaded Method(Some stuff I wanna do)
 }

如果你在没有任何回调的情况下运行它,你的程序看起来就像它只是退出一样。 因为“花哨的多线程东西”正在另一个进程上运行。

所以你挠了挠头,想着“好吧,我怎么知道什么时候完成??

繁荣。。。回调

IsItDone = false

Callback()
{
  print("Hey, I'm done")
  IsItDone = true
}
  
Multithreaded Method(Some arguments, Function callback)
  {
    Do fancy multithreaded stuff....
  }

Main()
 {
   Some stuff I wanna do = some tasks
   Multhreaded Method(Some stuff I wanna do,Callback)

   while(!IsItDone)
     Wait a bit
 }

这 100% 不是实现它的最佳方式,我只是想举一个明确的例子。

所以这不是赤裸裸的“什么是回调? 这是“什么是回调,它有什么作用使我受益???”