【问题标题】:What is a callback function?什么是回调函数?
【发布时间】:2010-10-23 21:06:41
【问题描述】:

什么是回调函数?

【问题讨论】:

标签: language-agnostic callback


【解决方案1】:

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

回调函数是一个函数:

  • 可由其他函数访问,并且
  • 如果第一个函数完成,则在第一个函数之后调用

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

“call after” 函数可能更好。

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

伪代码:

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

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

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

某些语言支持支持多个回调函数参数的结构,并根据父函数的完成方式调用(即,如果父函数成功完成,则调用一个回调,如果父函数成功完成,则调用另一个回调。函数抛出特定错误等)。

【讨论】:

  • 您的示例很棒,但我不明白为什么术语是“回调”。 meaningOfLife 什么时候“召回”?
  • 你好,关于once its parent method completes, the function which this argument represents is then called。因此,如果函数作为参数传递给另一个函数,但从父函数的运行时中间调用,如parent(cb) {dostuff1(); cb(); dostuff2()},那么它不被视为callback 函数?
  • @MaxYari:恕我直言,它仍然被认为是回调。这里重要的是父函数将以某种方式使用输入函数(又名回调)。它可以在中间或结尾或满足条件时调用。
  • @8bitjunkie 谢谢 - 但是 printANumber 函数中调用的 meaningOfLife 方法在哪里?
  • 这根本不是真的:“在第一个函数完成后自动调用”。根本不需要执行回调,更不用说自动执行了。事实上,回调在父函数完成之前完成并不少见。我非常不喜欢人们将回调描述为“稍后”执行的函数。对于正在学习它们的人来说,这非常令人困惑。简单地说,回调只是作为参数传递给其他函数的函数。时期。更好的解释包括解释为什么回调函数引用。
【解决方案2】:

不透明定义

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

人为的例子

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

  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) 一样小。如果您的语言要求您将其定义为一个单独的函数,并带有完全不必要的名称和签名,那么如果您经常使用这种模式,您的生活将会变得不愉快。

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

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

这样更好。

如何传递回调

如何将回调函数传递给factorial?好吧,你可以通过多种方式做到这一点。

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

  2. 或者你可能想在你的程序中维护一个fn name --> fn ptr的字典,在这种情况下你可以传递名字

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

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

你明白了。

近期回调的兴起

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

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

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

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

【讨论】:

  • 是的,我了解lambas 在javascript 和ruby 中的工作原理。和 java 8 但旧版本的 java 没有使用 lambas 而是使用类,我想知道这种回调是如何工作的。仍然是比另一个更好的答案。
  • 没有。 作为函数的每个参数都不是回调。 它可以是迭代器函数、排序函数或许多其他东西。请参阅术语“回调”的词源。
  • 可以公平地说,这种回调的使用是“远程过程调用”的一种形式吗?
【解决方案3】:

维基百科上的Callback page解释得很好:

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

【讨论】:

  • 这也导致了不同的答案。名词“回调”是被“回调”的东西,就像经历过关机的东西已经被关闭,用来登录的东西是登录一样。
  • 维基百科实际上有一些非常棒的编程知识。我一直觉得“回拨”这个词最好用“我要回拨到……”来解释
  • 在 javascriptissexy.com/ 上的精彩解释…;我将在这里重新发布;回调函数是作为参数传递给另一个函数的函数,回调函数在otherFunction内部被调用或执行。 //注意click方法的参数中的item是一个函数,不是变量。​ //item是一个回调函数 $("#btn_1").click(function() { alert("Btn 1 Clicked" ); });正如您在前面的示例中看到的,我们将一个函数作为参数传递给 click 方法以使其执行 -
  • @CodyBugstein 我认为这个问题很简单,维基百科页面解释了第一段中的概念,不需要更长的答案。例如,这里的top answer 只是用不同的词表达了与维基百科第一段相同的想法,对我来说,伪代码并没有显示维基百科示例所做的任何事情。 DRY ;-)
  • 这很有趣,因为这个答案引用了维基百科,它引用了来自 8bitjunkie 的 Stack Overflow 答案
【解决方案4】:

外行的反应是,它不是由您调用,而是由用户或浏览器在特定事件发生或处理某些代码后调用的函数。

【讨论】:

    【解决方案5】:

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

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

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

    【讨论】:

    • 在你的第一句话中,你说"...when a condition is met" 但我认为回调是在父函数完成执行并且不依赖于条件(?)时调用的。
    • “某些条件”只是意味着他们通常被调用是有原因的,而不是随机的。当父/创建者仍在执行时,可能会调用回调 - 如果程序员没有预料到,这可能会导致竞争条件。
    【解决方案6】:

    让我们保持简单。什么是回调函数?

    比喻和类比的例子

    我有一个秘书。每天我都会要求她:(i) 将公司寄出的邮件寄到邮局, 她完成之后,去做:(ii) 无论我写什么任务在其中一个sticky notes 上为她服务。

    现在,便签上的任务是什么?任务每天都在变化。

    假设在这一天,我要求她打印一些文件。所以我把它写在便利贴上,然后把它和她需要寄出的邮件一起钉在她的桌子上。

    总结:

    1. 首先,她需要投递邮件和
    2. 完成之后,她需要打印一些文件。

    回调函数是第二个任务:打印这些文档。因为它是在邮件被投递之后完成的,而且还因为告诉她打印文件的便签与她需要发布的邮件一起提供给她。

    现在让我们将其与编程词汇联系起来

    • 本例中的方法名称为:DropOffMail。
    • 而回调函数是:PrintOffDocuments。 PrintOffDocuments 是回调函数,因为我们希望秘书在 DropOffMail 运行之后才这样做。
    • 所以我会“将:PrintOffDocuments 作为“参数”传递给 DropOffMail 方法。这一点很重要。

    就是这样。而已。我希望这为您解决了问题 - 如果没有,请发表评论,我会尽力澄清。

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

      • 所以我的名字和号码是一个函数?
      • 不,如果“回拨”是一个好名字,那就是类比:您要求电话接线员拨打电话。结束。
      • 我从这个可爱的答案中推断出“回调”的行为是回调函数终止并返回到父函数时。我错了吗?
      【解决方案8】:

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

      回调函数是你传递给某人并让 他们会在某个时间点调用它。

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

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

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

      其实只有wiki定义中的第二句就揭示了回调函数和普通函数的区别:

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

      所以区别在于你将传递函数的人以及你传递的函数将如何被调用。如果您只是定义一个函数并将其传递给另一个函数并直接在该函数体中调用它,请不要将其称为回调。定义说你传入的函数将被“低级”函数调用。

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

      【讨论】:

      • 你的回答很有道理......但我无法想象它。能举个例子吗?
      • @Zane Wong :: 在最后你写了“定义说你传入的函数将被“低级”函数调用。”你能解释一下低级功能是什么意思吗?举个例子就更好了。
      • 举个例子就好了
      • 我认为经典函数调用和回调风格之间的区别在于依赖方向:如果模块A依赖(“使用”)模块B,A调用B的函数,它不是打回来。如果 A 将他的函数的引用传递给 B,则 B 调用 A 的函数,这是一个回调:与模块依赖相比,调用向后。
      • Mr @ZaneXY “如果您只是定义一个函数并将其传递给另一个函数并直接在该函数体中调用它,请不要将其称为回调。”这不是同步回调的定义,即立即执行的回调吗?
      【解决方案9】:

      回调与回调函数

      回调是在另一个函数执行完毕后执行的函数 — 因此得名‘回调’

      什么是回调函数

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

      Source

      【讨论】:

      • 回调函数本身并不是高阶函数。它被传递给一个高阶函数。
      【解决方案10】:

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

      我不确定它们是什么。

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

      我还认为回调旨在解决原始调用,以一种“嘿!你要求的那件事?我已经做到了 - 只是想我会让你知道 - 回到你身边”。

      【讨论】:

      • +1 用于质疑回调与返回语句。我曾经对此感到困惑,与我一起工作的许多毕业生也是如此。
      【解决方案11】:

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

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

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

      【讨论】:

        【解决方案12】:

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

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

        【讨论】:

        • 这是个好主意。我去“在后面打电话”试图解释这一点。我可以看到像 Martin Fowler 这样的人在他的开创性博客上将“call after”作为一个新术语来普及。
        【解决方案13】:

        看图片:)

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

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

        【讨论】:

        • 您介意为此添加一个文本解释吗?如果图像消失,则此答案将失去所有上下文。
        • 其他人的文字解释得最好。我唯一觉得缺少的是图像:)
        • 在我在这里看到的所有冗长的描述中,这一个是让我感到“啊哈,现在我看到它的用途”的那个。投赞成票。
        • 嗯,它不必是 库函数 以这种方式回叫您。您的程序可以创建一个单独的线程来执行某些任务并触发回调机制,而无需外部库。
        【解决方案14】:

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

        【讨论】:

          【解决方案15】:

          假设我们有一个函数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)`   ...
          

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

          【讨论】:

          • 在 javascriptissexy.com/ 上的精彩解释…;我将在这里重新发布;回调函数是作为参数传递给另一个函数的函数,回调函数在otherFunction内部被调用或执行。 //注意click方法的参数中的item是一个函数,不是变量。​ //item是一个回调函数 $("#btn_1").click(function() { alert("Btn 1 Clicked" ); });正如您在前面的示例中看到的,我们将一个函数作为参数传递给 click 方法以使其执行 -
          【解决方案16】:

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

          使用函数指针在 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没有函数指针的概念 它通过其Interface机制实现Callback机制 这里我们声明了一个接口,而不是函数指针,该接口有一个方法,当被调用者完成任务时将调用该方法

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

          回调接口

          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)实现了这两个接口。并且每当一个widget发生事件时,都会调用更高级的代码(或者说在更高级的代码中实现的这些接口的方法,这里是Activity)。

          现在让我讨论回调和事件监听器模式之间的基本区别。正如我们提到的,使用回调,被调用者只能通知一个调用者。但是在 EventListener 模式的情况下,应用程序的任何其他部分或类都可以注册可能发生在 Button 或 Checkbox 上的事件。这种类的例子是OtherClass。如果你看OtherClass的代码,你会发现它已经将自己注册为Activity中定义的Button中可能发生的ClickEvent的监听器。有趣的是,除了 Activity(调用者)之外,每当 Button 上发生点击事件时,也会通知这个 OtherClass。

          【讨论】:

          【解决方案17】:

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

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

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

          【讨论】:

            【解决方案18】:

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

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

            ...

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

            【讨论】:

              【解决方案19】:

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

              【讨论】:

                【解决方案20】:

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

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

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

                【讨论】:

                • 回调函数本身并不是高阶函数。它被传递给一个高阶函数。
                【解决方案21】:

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

                function test_function(){       
                 alert("Hello world");  
                } 
                
                setTimeout(test_function, 2000);
                

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

                【讨论】:

                • 欢迎来到 Stack Overflow!在回答问题之前,请务必阅读现有答案。这个答案已经提供了。不要重复答案,而是投票赞成现有的答案。可以在here 找到一些编写好答案的指南。
                猜你喜欢
                • 2012-08-04
                • 1970-01-01
                • 1970-01-01
                • 2019-10-26
                • 1970-01-01
                • 2020-10-10
                • 2022-12-31
                相关资源
                最近更新 更多