如何用简单的英语解释回调?

How to explain callbacks in plain english? How are they different from calling one function from another function?

如何用简单的英语解释回拨?它们与从调用函数获取上下文的另一个函数调用一个函数有什么不同?如何向初学者解释他们的能力?


我要尽量让这件事简单些。"回调"是由另一个以第一个函数为参数的函数调用的任何函数。很多时候,"回调"是一个函数,当发生某些事情时调用它。在程序员的语言中可以称之为"事件"。

想象一下这个场景:您在几天内就要收到一个包裹。这个包裹是送给邻居的礼物。因此,一旦你拿到包裹,你就要把它交给邻居。你不在城里,所以你给你的配偶留下指示。

你可以让他们把包裹拿到邻居那里。如果你的配偶像电脑一样笨,他们会坐在门口等包裹来了(不做任何其他事情),然后等它来了,他们会把它带到邻居家。但有更好的方法。告诉你的配偶,一旦他们收到包裹,他们就应该把它交给邻居。然后,他们可以正常生活,直到他们收到包裹。

在我们的示例中,接收包是"事件",而将它带给邻居是"回调"。你的配偶"运行"你的指示,只有当包裹到达时才带上包裹。好多了!

这种想法在日常生活中是显而易见的,但计算机没有同样的常识。考虑程序员通常如何写入文件:

1
2
3
4
fileObject = open(file)
# now that we have WAITED for the file to open, we can write to it
fileObject.write("We are writing to the file.")
# now we can continue doing the other, totally unrelated things our program does

在这里,我们等待文件打开,然后再写入。这"阻塞"了执行流,我们的程序不能做它可能需要做的任何其他事情!如果我们可以改为这样做:

1
2
3
4
# we pass writeToFile (A CALLBACK FUNCTION!) to the open function
fileObject = open(file, writeToFile)
# execution continues flowing -- we don't wait for the file to be opened
# ONCE the file is opened we write to it, but while we wait WE CAN DO OTHER THINGS!

事实证明,我们用一些语言和框架来实现这一点。很酷!查看node.js以获得这种思考的一些实际实践。


通常,应用程序需要根据其上下文/状态执行不同的函数。为此,我们使用一个变量来存储关于要调用的函数的信息。?根据需要,应用程序将用要调用的函数的信息设置该变量,并使用相同的变量调用该函数。

在javascript中,示例如下。这里我们使用方法参数作为变量来存储函数的信息。

1
2
3
4
5
6
7
8
9
10
function processArray(arr, callback) {
    var resultArr = new Array();
    for (var i = arr.length-1; i >= 0; i--)
        resultArr[i] = callback(arr[i]);
    return resultArr;
}

var arr = [1, 2, 3, 4];
var arrReturned = processArray(arr, function(arg) {return arg * -1;});
// arrReturned would be [-1, -2, -3, -4]


How to explain callbacks in plain English?

用简单的英语来说,回调函数就像一个工人在完成一项任务后"回拨"给他的经理。

How are they different from calling one function from another function
taking some context from the calling function?

的确,您正在从另一个函数调用一个函数,但关键是回调被视为一个对象,因此您可以根据系统的状态(如策略设计模式)更改要调用的函数。

How can their power be explained to a novice programmer?

在需要从服务器中提取数据的Ajax风格的网站中,很容易看到回调的威力。下载新数据可能需要一些时间。如果没有回调,下载新数据时整个用户界面将"冻结",或者需要刷新整个页面,而不仅仅是其中的一部分。通过回调,可以插入"正在加载"图像,并在加载后用新数据替换它。

一些没有回调的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function grabAndFreeze() {
    showNowLoading(true);
    var jsondata = getData('http://yourserver.com/data/messages.json');
    /* User Interface 'freezes' while getting data */
    processData(jsondata);
    showNowLoading(false);
    do_other_stuff(); // not called until data fully downloaded
}

function processData(jsondata) { // do something with the data
   var count = jsondata.results ? jsondata.results.length : 0;
   $('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
   $('#results_messages').html(jsondata.results || '(no new messages)');
}

回调:

下面是一个使用jquery的getjson进行回调的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function processDataCB(jsondata) { // callback: update UI with results
   showNowLoading(false);
   var count = jsondata.results ? jsondata.results.length : 0;
   $('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
   $('#results_messages').html(jsondata.results || '(no new messages)');
}

function grabAndGo() { // and don't freeze
    showNowLoading(true);
    $('#results_messages').html(now_loading_image);
    $.getJSON("http://yourserver.com/data/messages.json", processDataCB);
    /* Call processDataCB when data is downloaded, no frozen User Interface! */
    do_other_stuff(); // called immediately
}

关闭:

通常回调需要使用closure从调用函数访问state,这就像工人在完成任务之前需要从经理那里获得信息一样。要创建closure,可以内联函数,以便它在调用上下文中看到数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* Grab messages, chat users, etc by changing dtable. Run callback cb when done.*/
function grab(dtable, cb) {
    if (null == dtable) { dtable ="messages"; }
    var uiElem ="_" + dtable;
    showNowLoading(true, dtable);
    $('#results' + uiElem).html(now_loading_image);
    $.getJSON("http://yourserver.com/user/"+dtable+".json", cb || function (jsondata) {
       // Using a closure: can"see" dtable argument and uiElem variables above.
       var count = jsondata.results ? jsondata.results.length : 0,
           counterMsg = ['Fetched', count, 'new', dtable].join(' '),
           // no new chatters/messages/etc
           defaultResultsMsg = ['(no new ', dtable, ')'].join('');
       showNowLoading(false, dtable);
       $('#counter' + uiElem).text(counterMsg);
       $('#results'+ uiElem).html(jsondata.results || defaultResultsMsg);
    });
    /* User Interface calls cb when data is downloaded */

    do_other_stuff(); // called immediately
}

用途:

1
2
3
4
5
6
// update results_chatters when chatters.json data is downloaded:
grab("chatters");
// update results_messages when messages.json data is downloaded
grab("messages");
// call myCallback(jsondata) when"history.json" data is loaded:
grab("history", myCallback);

关闭

最后,这里是Douglas Crockford对closure的定义:

函数可以在其他函数内部定义。内部函数可以访问外部函数的变量和参数。如果对内部函数的引用继续存在(例如,作为回调函数),则外部函数的var也会继续存在。

参见:

  • http://javascript.crockford.com/survey.html网站
  • http://api.jquery.com/jquery.when/
  • http://api.jquery.com/jquery.getjson/
  • http://github.com/josher19/jquery-parse


我很惊讶看到这么多聪明的人没有强调"回调"这个词已经被用在两个不一致的方面。

这两种方法都涉及通过将附加功能(匿名或命名的函数定义)传递给现有函数来定制函数。IE.

1
customizableFunc(customFunctionality)

如果自定义功能只是插入到代码块中,那么您已经像这样自定义了该功能。

1
2
3
4
5
    customizableFucn(customFunctionality) {
      var data = doSomthing();
      customFunctionality(data);
      ...
    }

尽管这种注入的功能通常被称为"回调",但它并没有什么偶然性。一个非常明显的例子是foreach方法,其中一个自定义函数作为参数提供给数组中的每个元素以修改数组。

但这与异步编程中使用"回调"函数有着根本的区别,比如在Ajax或node.js中,或者只是在为用户交互事件分配功能(如鼠标单击)时。在这种情况下,整个想法是在执行自定义功能之前等待一个偶然事件发生。这在用户交互的情况下很明显,但在I/O(输入/输出)过程中也很重要,这可能需要一些时间,比如从磁盘读取文件。这就是"回调"这个词最明显的意义所在。一旦启动了一个I/O进程(比如请求从磁盘读取文件,或者请求服务器从HTTP请求返回数据),异步程序就不会等待它完成。它可以继续执行下一步计划的任何任务,并且只有在通知了读取文件或HTTP请求已完成(或失败)并且数据可用于自定义功能之后,才会使用自定义功能进行响应。这就像打电话给一家公司,留下你的"回拨"号码,这样他们可以在有人可以回你电话时给你打电话。这总比为谁知道要多久不能处理其他事情而挂断电话要好。

异步使用固有地涉及一些监听所需事件的方法(例如,完成I/O过程),以便在发生(并且仅在发生)时执行自定义"回调"功能。在明显的Ajax示例中,当数据实际从服务器到达时,会触发"回调"函数以使用该数据修改DOM,从而重新绘制浏览器窗口。

重述有些人使用"回调"这个词来指代任何可以作为参数注入到现有函数中的自定义功能。但是,至少对我来说,这个词最合适的用法是异步使用注入的"回调"函数——仅在等待通知的事件发生时执行。


在非程序员术语中,回调是程序中空白的填充。

许多纸质表格上的一个常见项目是"紧急情况下要打电话的人"。那里有一个空行。你写下某人的名字和电话号码。如果发生紧急情况,则该人会被呼叫。

  • 每个人都得到相同的空白表格,但是
  • 每个人都可以写一个不同的紧急联系电话。

这是关键。您不会更改表单(代码,通常是其他人的)。但是,您可以填写缺少的信息(您的号码)。

例1:

回调被用作自定义方法,可能用于添加/更改程序的行为。例如,使用一些执行函数但不知道如何打印输出的C代码。它所能做的就是做一根绳子。当它试图弄清楚如何处理字符串时,会看到一条空行。但是,程序员给你写回调的空白!

在本例中,不使用铅笔在纸上填写空白,而是使用函数set_print_callback(the_callback)

  • 模块/代码中的空白变量是空白行,
  • set_print_callback是铅笔,
  • 你要填写的信息是the_callback

您现在已经在程序中填写了这一空行。每当它需要打印输出时,它都会查看这一空白行,并遵循其中的说明(即调用您在其中放置的函数)。实际上,这允许打印到屏幕、日志文件、打印机、网络连接或其任何组合。你把你想做的事填空了。

例2:

当你被告知需要打紧急电话时,你去读写在纸上的东西,然后打电话给你读到的号码。如果那一行是空的,什么都不做。

图形用户界面编程的工作方式大致相同。单击按钮时,程序需要确定下一步要做什么。它去寻找回调。此回调恰好位于一个空白处,标记为"单击按钮1时的操作如下"

大多数IDE将自动为您填写空白(写入基本方法),当您要求它时(例如EDCOX1(3))。然而,空白可以有任何方法,你很好,请。只要将回调的名称放在适当的空白中,就可以调用方法run_computationsbutter_the_biscuits。您可以将"555-555-1212"放在紧急号码空白处。这没什么意义,但这是允许的。

最后一个注意事项:您要用回调填充的空白行?它可以随意删除和重写。(你是否应该是另一个问题,但这是他们权力的一部分)


程序员约翰尼需要一台订书机,所以他到办公室的供应部去要一台,填好申请表后,他要么站在那里等店员,到仓库里四处寻找订书机(就像一个阻塞函数调用),要么同时做些别的事情。

因为这通常需要时间,约翰尼把一张便条和申请表放在一起,要求他们在订书机准备好取书时给他打电话,这样他就可以去做其他事情,比如在桌子上打盹。


最好从一个例子开始:)。

假设您有两个模块A和B。

当模块B中发生某些事件/条件时,您希望模块A得到通知。但是,模块B不知道您的模块A。它只知道通过模块A提供给它的函数指针指向特定函数(模块A)的地址。

所以现在需要做的就是,当使用函数指针发生特定的事件/条件时,将"回调"到模块A中。a可以在回调函数内部做进一步的处理。

*)这里的一个明显优势是,你从模块B中抽象出了模块A的所有内容。模块B不必关心模块A是谁/是什么。


假设您需要一个返回10平方的函数来编写一个函数:

1
function tenSquared() {return 10*10;}

稍后,您需要9个平方,以便编写另一个函数:

1
function nineSquared() {return 9*9;}

最终,您将用一个通用函数替换所有这些函数:

1
function square(x) {return x*x;}

同样的想法也适用于回调。您有一个函数可以执行某些操作,当完成时调用DOA:

1
2
3
4
function computeA(){
    ...
    doA(result);
}

稍后,您希望使用完全相同的函数来调用dob,而可以复制整个函数:

1
2
3
4
function computeB(){
    ...
    doB(result);
}

或者,您可以将回调函数作为变量传递,并且只需具有该函数一次:

1
2
3
4
function compute(callback){
    ...
    callback(result);
}

然后你只需要调用compute(doa)和compute(dob)。

除了简化代码之外,它还允许异步代码通过在完成时调用任意函数来让您知道它已经完成,类似于在电话中呼叫某人并留下回拨号码。


你觉得不舒服就去看医生。他检查了你,确定你需要一些药物。他开了一些药,把处方打到你当地的药房。你回家吧。稍后你的药房打电话告诉你你的处方已经准备好了。你去把它捡起来。


有两点需要解释,一个是回调是如何工作的(传递一个可以在不了解上下文的情况下调用的函数),另一个是回调的用途(异步处理事件)。

类似于等待一个包裹到达,其他答案都用它来解释这两个问题,这是一个很好的例子。在计算机程序中,你会告诉计算机要一个包裹。通常情况下,它现在会坐在那里等待(不做任何其他事情),直到包裹到达,如果它从未到达,可能是无限期的。对人类来说,这听起来很愚蠢,但如果不采取进一步的措施,这对计算机来说是完全自然的。

现在回拨电话就是你前门的铃了。您为包裹服务提供了一种通知您包裹到达的方式,而不必让他们知道您在哪里(即使是在家里),也不必知道铃是如何工作的。(例如,一些"铃"实际上发出了一个电话呼叫。)因为您提供了一个"回调函数",可以在任何时候"调用",脱离上下文,现在您可以随时停止坐在前廊和"处理事件"(包裹到达)。


您有一些要运行的代码。通常,当你调用它时,你会在继续之前等待它完成(这会导致你的应用程序变灰/产生光标旋转时间)。

另一种方法是并行运行此代码并继续您自己的工作。但是,如果原始代码需要根据它调用的代码的响应来做不同的事情呢?那么,在这种情况下,您可以在代码完成后输入您希望它调用的代码的名称/位置。这是一个"回电"。

常规代码:询问信息->处理信息->处理处理结果->继续做其他事情。

使用回调:询问信息->处理信息->继续做其他事情。然后在以后的某个时候->处理处理结果。


没有其他特殊的编程资源回调曼弗雷迪或法拉利吗(像线程,和其他),确切的说因为是教学计划是在executed of which are序列另一序列加法标号.后,甚至用kind of"和动态行为"模式的矛盾在一定条件previously可能的场景,将好的程序升温。P></

我知道,如果我们需要提供真正的动态行为的程序我们可以使用回调。你可以instructs回调参数和模式,提供另一个程序呼叫(to some previously安可以定义的参数和结果expects some(this is the contract or operation signature),这些结果可以知道生产/加工模式三党正计划previously which known。P></

This is the Foundation of子的多态性技术应用到项目的目标,功能,和其他unities备用计算机代码之然。P></

"used as to the World example is explained回调是好当你做一些工作,你是在想让画家(the main program在这里你是,那你的画)和呼叫客户端有时问他to the result of your job的批准决定,他知道,如果你的客户是好的(the Picture is茶党(三)。P></

你是在the above a example"画家和委托人"to the job to the result的批准,the Picture is the New布尔参数,每个客户端(所谓的"功能""the result of the changes)你什么deciding about the work he wants决策自制by the Picture(the result clients are returned the from the"回调函数")。P></

我希望这可以是有用的解释。P></


我们无法假装你是给我potentially跨度任务:get the running of the first names)人在独特的你。如果我可以把这一天在sparsely populated地区。你不是真的你的手在我的兴趣(我知道你跑的时候说,"你要我呼叫我the list,细胞在线读回来给我。这是the number。"。P></

给我你的回调函数参考-现在我做的手关闭to execute以继续处理。P></

它的外观可以在JavaScript这样的东西:P></

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var lottoNumbers = [];
var callback = function(theNames) {
  for (var i=0; i<theNames.length; i++) {
    lottoNumbers.push(theNames[i].length);
  }
};

db.executeQuery("SELECT name" +
               "FROM tblEveryOneInTheWholeWorld" +
               "ORDER BY proximity DESC" +
               "LIMIT 5", callback);

while (lottoNumbers.length < 5) {
  playGolf();
}
playLotto(lottoNumbers);

这可以采取很多方式在of probably be。例如,你可以提供在第二端以回调:如果它比上周安小时,茶叶和茶红手机呼叫回答,那人告诉你时间了。P></


想象一个朋友要离开你的房子,你告诉她"回家后给我打电话,这样我就知道你安全到达了";这(字面上)是一个回电。不管语言如何,回调函数就是这样的。您需要一些过程在控制完成某些任务后将其传递回您,因此您为它提供了一个函数来用于回调。

例如,在python中,

1
grabDBValue( (lambda x: passValueToGUIWindow(x) ))

可以写入grabDBValue以仅从数据库中获取一个值,然后让您指定实际如何处理该值,因此它接受一个函数。你不知道grabDBValue什么时候或什么时候会回来,但如果/什么时候回来,你知道你想要它做什么。在这里,我传递一个匿名函数(或lambda),它将值发送到一个GUI窗口。我可以通过这样做很容易地改变程序的行为:

1
grabDBValue( (lambda x: passToLogger(x) ))

回调在函数为第一类值的语言中运行良好,就像通常的整数、字符串、布尔值等。在C中,可以通过传递一个指针来"传递"一个函数,调用方可以使用它;在Java中,调用方将请求具有特定方法名称的特定类型的静态类,因为有N个指针。o类之外的函数("methods",真的);在大多数其他动态语言中,只需通过简单语法传递函数即可。

Protip:

在具有词汇范围的语言中(如Scheme或Perl),您可以使用以下技巧:

1
2
3
my $var = 2;
my $val = someCallerBackFunction(sub callback { return $var * 3; });
# Perlistas note: I know the sub doesn't need a name, this is for illustration

在这种情况下,$val将是6,因为回调可以访问在定义它的词汇环境中声明的变量。词法范围和匿名回调是一个强大的组合,保证了对初学者的进一步研究。


回拨最容易用电话系统来描述。函数调用类似于打电话给某人,问她一个问题,得到一个答案,然后挂断电话;添加一个回调会改变这个类比,这样在问她一个问题之后,你也会给她你的名字和号码,这样她就可以用这个答案给你回电话。——Paul Jakubik"C++中的回调实现"


回调是将由第二个函数调用的函数。第二个函数事先不知道它将调用什么函数。所以回调函数的标识存储在某个地方,或者作为参数传递给第二个函数。根据编程语言的不同,这个"标识"可能是回调的地址或其他类型的指针,也可能是函数的名称。主体是相同的,我们存储或传递一些明确标识函数的信息。

当时间到来时,第二个函数可以调用回调,根据当时的情况提供参数。它甚至可以从一组可能的回调中选择回调。编程语言必须提供某种语法,允许第二个函数调用回调,知道它的"标识"。

这种机制有许多可能的用途。通过回调,函数的设计者可以通过让函数调用提供的回调来定制它。例如,排序函数可以将回调作为参数,并且此回调可能是一个用于比较两个元素以决定哪个元素在前面的函数。

另外,根据编程语言的不同,上述讨论中的"函数"一词可能会被"块"、"闭包"、"lambda"等替换。


通常我们将变量发送到函数。假设您有一个任务,在将变量作为参数给定之前需要对其进行处理—您可以使用回调。

通常的方法是使用function1(var1, var2)

如果我希望处理var2然后作为参数发送,该怎么办?function1(var1, function2(var2))

这是一种回调类型,其中function2执行一些代码并将变量返回到初始函数。


要教回拨,你必须先教指针。一旦学生理解了指向变量的指针的概念,回调的概念就会变得更容易。假设您正在使用C/C++,可以遵循这些步骤。

  • 首先向您的学生演示如何使用指针以及使用普通变量标识符来使用和操作变量。
  • 然后教他们只有指针才能完成一些事情(比如通过引用传递变量)。
  • 然后告诉他们可执行代码或函数是如何像内存中的其他数据(或变量)一样的。因此,函数也有地址或指针。
  • 然后向他们展示如何使用函数指针调用函数,并告诉他们如何调用这些函数。
  • 现在,问题是,为什么调用某些函数会有这么多麻烦?有什么好处?和数据指针一样,函数指针aka回调比使用普通标识符有一些优势。
  • 第一个是,函数标识符或函数名不能用作普通数据。我的意思是,您不能用函数(如数组或函数的链接列表)创建数据结构。但是,通过回调,您可以创建一个数组、一个链接列表,或者将它们与其他数据一起使用,比如在键值对或树的字典中,或者其他任何东西。这是一个强有力的好处。其他的好处其实是这个孩子的。
  • 回调的最常见用法出现在事件驱动程序编程中。其中一个或多个功能是基于某个输入信号执行的。通过回调,可以维护字典来映射带有回调的信号。然后输入信号的分辨率和相应代码的执行变得更加容易。
  • 在我看来,回调的第二个用途是高阶函数。将其他函数作为输入参数的函数。为了将函数作为参数发送,我们需要回调。示例可以是接受数组和回调的函数。然后,它对数组的每个项执行回调,并将结果返回到另一个数组中。如果我们给函数传递一个双重回调,就会得到一个双值数组。如果我们通过一个平方回拨,我们得到平方。对于平方根,只需发送适当的回调。这不能用正常功能来完成。

可能还有很多事情。让学生参与进来,他们会发现。希望这有帮助。


隐喻性解释:

我有一个包裹要交给一个朋友,我还想知道我朋友什么时候收到。

所以我把包裹拿到邮局,让他们送去。如果我想知道我朋友什么时候收到包裹,我有两个选择:

(a)我可以在邮局等邮件送达。

(b)我收到邮件后会收到。

选项(b)类似于回调。


用简单的英语来说,回拨是一种承诺。乔、简、大卫和萨曼莎共用一辆拼车去上班。乔今天开车。简、大卫和萨曼莎有几个选择:

  • 每5分钟检查一次窗户看看乔是否出去了
  • 继续做他们的事,直到乔按了门铃。
  • 选项1:这更像是一个投票的例子,简会被困在一个"循环"中,检查乔是否在外面。简不能同时做别的事。

    选项2:这是回调示例。简让乔在外面按门铃。她给他一个按门铃的"功能"。乔不需要知道门铃是如何工作的,也不需要知道它在哪里,他只需要调用这个函数,即当他在那里的时候按门铃。

    回调由"事件"驱动。在这个例子中,"事件"是乔的到来。例如,在Ajax中,事件可以是异步请求的"成功"或"失败",并且每个事件都可以有相同或不同的回调。

    在javascript应用程序和回调方面。我们还需要了解"闭包"和应用程序上下文。"this"所指的内容很容易让JavaScript开发人员感到困惑。在这个例子中,在每个人的"ring the_door_bell()"方法/回调中,可能还有一些其他方法需要每个人根据他们的早晨例行程序执行,例如"turn_off_the_tv()"。我们希望"this"指的是"jane"对象或"david"对象,这样每个对象都可以在joe拿起它们之前设置它们需要做的任何其他操作。在这里,使用Joe设置回调需要对方法进行模仿,以便"this"引用正确的对象。

    希望有帮助!


    在回调是自addressed stamped信封。当你在function呼叫,that is like a letter发送。如果你想你的呼叫另一个函数function to that that the form of提供信息参考or address。P></


    我认为这是相当容易的工作也解释。P></

    回调函数是在第一只是一般。is that the further and this function(呼叫我们,让我们从另一个呼叫EN()函数在我们的呼叫(b)。P></

    魔术about this is that the which should be函数决定的,所谓的"the function从B模式。P></

    函数write the at the time i b我不知道which should be called回调函数。at the time函数B呼叫呼叫函数function to this also tell that is a。在。P></


    什么是回调函数?

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

    回调函数很难跟踪,但有时非常有用。尤其是在设计图书馆时。回调函数类似于要求用户给您一个函数名,您将在特定条件下调用该函数。

    例如,编写回调计时器。它允许您指定持续时间和要调用的函数,并相应地回调函数。"每10秒运行MyFunction()5次"

    或者,您可以创建一个函数目录,传递函数名列表,并要求库进行相应的回调。回调成功()如果成功,回调失败()如果失败。

    让我们看一个简单的函数指针示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    void cbfunc()
    {
         printf("called");
    }

     int main ()
     {
                       /* function pointer */
          void (*callback)(void);
                       /* point to your callback function */
          callback=(void *)cbfunc;
                       /* perform callback */
          callback();
          return 0;
    }

    如何将参数传递给回调函数?

    注意到用于实现回调的函数指针接受void*,这表示它可以接受任何类型的变量,包括结构。因此,可以按结构传递多个参数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    typedef struct myst
    {
         int a;
         char b[10];
    }myst;

    void cbfunc(myst *mt)
    {
         fprintf(stdout,"called %d %s.",mt->a,mt->b);
    }

    int main()
    {
           /* func pointer */
        void (*callback)(void *);       //param
         myst m;
         m.a=10;
         strcpy(m.b,"123");      
         callback = (void*)cbfunc;    /* point to callback function */
         callback(&m);                /* perform callback and pass in the param */
         return 0;  
    }

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

    使用函数指针在C中回调好的。

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

    例如,int(*ptrfunc)();好的。

    这里,ptrfunc是指向一个不带参数并返回整数的函数的指针。不要忘记放在括号中,否则编译器将假定ptrfunc是一个正常的函数名,它不带任何内容,并返回一个指向整数的指针。好的。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #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
    ",result1,result2);
        return 0;
    }

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    /* callback.c */
    #include<stdio.h>
    #include"reg_callback.h"

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

    int main(void)
    {
        /* initialize function pointer to
        my_callback */
        callback ptr_my_callback=my_callback;                        
        printf("This is a program demonstrating function callback
    ");
        /* register our callback function */
        register_callback(ptr_my_callback);                          
        printf("back inside main program
    ");
        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
    ");
        /* calling our callback function my_callback */
        (*ptr_reg_callback)();                              
    }

    如果我们运行这个程序,输出将好的。

    这是一个演示函数回调的程序内部寄存器回调在我的回调中回到主程序内部好的。

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

    Java接口中的回调好的。

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

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

    回调接口好的。

    1
    2
    3
    4
    public interface Callback
    {
        public void notify(Result result);
    }

    调用方或更高级别的类好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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
    }
    }

    被调用方或下层函数好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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个观察者/监听者特定任务已完成。好的。

    • 列表项

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

    让我用一个例子来解释一下。好的。

    事件接口好的。

    1
    2
    3
    4
    5
    public interface Events {

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

    类控件好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    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);
        }
    }

    类按钮好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Button extends Widget{
    private String mButtonText;
    public Button (){
    }
    public String getButtonText() {
    return mButtonText;
    }
    public void setButtonText(String buttonText) {
    this.mButtonText = buttonText;
    }
    }

    类复选框好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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;好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    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();
        }  
    }

    其他班好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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...");
    }
    }

    主类好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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);
    }
    }

    从上面的代码中可以看到,我们有一个名为事件的接口,它基本上列出了应用程序可能发生的所有事件。widget类是所有UI组件(如button、checkbox)的基类。这些UI组件是实际从框架代码接收事件的对象。widget类实现事件接口,并且它有两个嵌套接口,即onclickEventListener&onlongclickEventListener好的。

    这两个接口负责监听小部件派生的UI组件(如按钮或复选框)上可能发生的事件。因此,如果我们将此示例与使用Java接口的较早的回调示例进行比较,这两个接口将作为回调接口。所以更高级别的代码(这里的活动)实现了这两个接口。每当小部件发生事件时,将调用更高级别的代码(或在更高级别的代码中实现的这些接口的方法,即这里的活动)。好的。

    现在让我来讨论回调和EventListener模式之间的基本区别。正如我们提到的,使用回调,被调用方只能通知单个调用方。但是,对于EventListener模式,应用程序的任何其他部分或类都可以注册按钮或复选框上可能发生的事件。这种类的例子是OtherClass。如果看到OtherClass的代码,您会发现它已经将自己注册为ClickEvent的侦听器,而ClickEvent可能出现在活动中定义的按钮中。有趣的是,除了活动(调用者),每当按钮上发生Click事件时,也会通知OtherClass。好的。好啊。


    平原和简单的:在你的回调函数是在给另一个函数,这样它可以呼叫它。P></

    usually en is called when some operation is completed。自从你给它创建回调before the function to the other,You can from the context初始化它与呼叫中心的信息。that is is named恩*为什么*呼叫功能在后台第一后台into the from where the context它是所谓的。P></


    回调是在满足条件时计划执行的方法。

    一个"真实世界"的例子是当地的电子游戏商店。你在等待半衰期3。你不必每天去商店看它是否在里面,而是把你的电子邮件注册到一个列表上,当游戏有空的时候就会收到通知。电子邮件将成为您的"回拨",需要满足的条件是游戏的可用性。

    "程序员"示例是一个网页,在该网页中,您希望在单击按钮时执行操作。为按钮注册回调方法并继续执行其他任务。当/如果用户点击按钮,浏览器将查看该事件的回调列表并调用您的方法。

    回调是异步处理事件的一种方法。您永远不知道何时执行回调,或者是否执行回调。其优点是在等待回复时,它可以释放程序和CPU周期来执行其他任务。


    把一个方法想象成给同事一个任务。一个简单的任务可能是:

    1
    2
    3
    Solve these equations:
    x + 2 = y
    2 * x = 3 * y

    你的同事勤奋地做数学并给你以下结果:

    1
    2
    x = -6
    y = -4

    但是你的同事有个问题,他不总是理解符号,比如^,但他确实通过它们的描述理解它们。如exponent。每次他发现其中一个,你都会得到以下信息:

    1
    I don't understand"^"

    这就要求你在向你的同事解释了这个角色对你的意义之后,重新编写你的整个指令集,而他在两个问题之间并不总是记得。他也很难记住你的建议,比如问我。他总是尽他所能地听从你的书面指示。

    如果你想到一个解决方案,你只需在你的所有说明中添加以下内容:

    1
    If you have any questions about symbols, call me at extension 1234 and I will tell you its name.

    现在,每当他有问题时,他都会打电话问你,而不是给你一个不好的答复,让进程重新启动。


    [编辑]当我们有两个函数时,即functiona和functionb,如果functiona依赖于functionb。

    然后我们调用函数b作为回调函数,这在Spring框架中得到了广泛的应用。

    callback function wikipedia example


    就下载网页而言:

    您的程序运行在手机上,并请求访问网页http://www.google.com。如果您同步编写程序,那么您为下载数据而编写的函数将持续运行,直到所有数据都被下载。这意味着你的用户界面不会刷新,基本上会冻结。如果使用回调编写程序,则请求数据并说"完成后执行此函数"。这允许用户界面在下载文件时仍允许用户交互。网页下载完成后,将调用结果函数(回调),您可以处理数据。

    基本上,它允许您请求一些东西并在等待结果的同时继续执行。一旦结果通过回调函数返回给您,您就可以在它停止的地方执行操作。


    回调允许您将自己的代码插入另一个代码块中,以便在另一时间执行,从而修改或添加另一个代码块的行为以满足您的需要。您获得了灵活性和可定制性,同时能够拥有更多可维护的代码。

    更少的硬编码=更容易维护和更改=更少的时间=更多的业务价值=卓越。

    例如,在javascript中,使用underline.js,可以在这样的数组中找到所有偶数元素:

    1
    2
    var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
    => [2, 4, 6]

    示例:http://documentcloud.github.com/underline/filter


    回调函数:

    我们定义一个名为callbackcallback function给它一个参数otherFunction并在函数体内部调用它。

    1
    2
    3
    function callback(otherFunction){
        otherFunction();
    }

    当我们调用callback函数时,它需要一个function类型的参数,因此我们用一个匿名函数来调用它。但是,如果参数不是function类型,则会产生错误。

    1
    2
    callback(function(){console.log('SUCCESS!')});
    callback(1); // error

    烤披萨的例子。烤箱烘烤配料披萨在这里,ovencallback function。而pizza base with ingredients就是otherFunction

    需要注意的是,不同的比萨配料生产不同类型的比萨,但烘烤比萨的烤箱保持不变。这在某种程度上是callback function的工作,它不断地期望具有不同功能的功能,以产生不同的定制结果。


    在PHP中,它类似于:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <?php

    function string($string, $callback) {
         $results = array(
            'upper' => strtoupper($string),
            'lower' => strtolower($string),
            );

        if(is_callable($callback)) {
            call_user_func($callback, $results);
        }
    }

    string('Alex', function($name) {
        echo $name['lower'];
    });?