What is a callback function?
什么是回调函数?
开发人员经常被什么是回调所混淆,因为这个该死的东西的名字。
回调函数是一个函数,它是:
- 可由其他功能访问,以及
- 如果第一个函数完成,则在第一个函数之后调用
想象回调函数如何工作的一个好方法是,它是一个函数,在它被传递到的函数的后面被调用。
也许一个更好的名字是"后调用"函数。
此构造对于异步行为非常有用,我们希望在上一个事件完成时发生活动。
Pseudocode:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 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(),则返回结果:
1 2 | The number you provided is: 6 I have finished printing numbers. |
这里的输出顺序很重要。由于随后调用回调函数,"我已完成打印数字"是最后打印的,而不是第一个。
回调之所以被称为回调,是因为它们与指针语言一起使用。如果你不使用其中的一个,不要为"回调"这个名字费劲。只需理解,它只是一个名称,用来描述作为另一个方法的参数提供的方法,这样当调用父方法(无论什么条件,如按钮单击、计时器勾号等)及其方法体完成时,都会调用回调函数。
有些语言支持支持支持多个回调函数参数的构造,并根据父函数的完成方式进行调用(即,在父函数成功完成时调用一个回调,在父函数引发特定错误时调用另一个回调等)。
不透明定义
回调函数是您提供给另一段代码的函数,允许该代码调用它。好的。人为的例子
你为什么要这么做?假设您需要调用一个服务。如果服务立即返回,您只需:好的。
例如,假设服务是
您当前的执行位置已保存(在堆栈上,但这并不重要)好的。
执行移交给
当
执行回到它原来的位置[1]好的。
现在假设
保持你的设计并在晚上睡觉时运行你的程序,这样你就不会有一半时间盯着屏幕看了。好的。
当
如果选择第二个选项,那么回调可能对您有效。好的。端到端设计
为了利用回调模式,您希望能够通过以下方式调用
1 | factorial(really_big_number, what_to_do_with_the_result) |
第二个参数,
是的,这意味着需要编写
现在假设您希望能够向回调传递参数。现在你不能了,因为你不会打电话给它,
1 2 3 4 5 | factorial (number, callback, params) { result = number! // i can make up operators in my pseudocode callback (result, params) } |
既然
1 2 3 4 | logIt (number, logger) { logger.log(number) } |
你打给EDOCX1的电话好的。
1 | factorial(42, logIt, logger) |
如果你想从
那么,为什么
由于执行是在
1 2 3 4 5 | factorial(param_1, param_2, ...) { new factorial_worker_task(param_1, param_2, ...); return; } |
现在这是一个"异步调用",也就是说,当您调用它时,它会立即返回,但还没有真正完成它的工作。因此,您确实需要机制来检查它,并在它完成时获得结果,并且您的程序在这个过程中变得更加复杂。好的。
另外,使用这种模式,
答案是保持在回调模式中。无论何时你想写好的。
1 2 | a = f() g(a) |
而
1 | f(g) |
其中
这从根本上改变了程序的流拓扑结构,需要一些适应。好的。
您的编程语言可以为您提供一种动态创建函数的方法,从而为您提供很多帮助。在上面的代码中,函数
另一方面,如果您的语言允许您创建lambda,那么您的形状就更好了。然后你会写一些好的。
1 | f( func(a) { print(2*a+1); }) |
这样好多了。好的。如何通过回调
如何将回调函数传递给
如果被调用函数在同一进程中运行,则可以传递函数指针好的。
或者你可能想在你的程序中维护一个
也许您的语言允许您就地定义函数,可能是lambda!在内部,它正在创建某种对象并传递指针,但您不必为此担心。好的。
也许您正在调用的函数运行在一台完全独立的机器上,并且您正在使用类似HTTP的网络协议来调用它。您可以将回调公开为HTTP可调用函数,并传递其URL。好的。
你明白了。好的。最近回调的兴起
在我们已经进入的这个网络时代,我们调用的服务通常是通过网络进行的。我们通常对这些服务没有任何控制权,即我们没有写它们,我们没有维护它们,我们无法确保它们正常运行,或者它们的性能如何。好的。
但在等待这些服务响应时,我们不能期望我们的程序阻塞。意识到这一点,服务提供者通常使用回调模式设计API。好的。
javascript非常支持回调,例如使用lambda和闭包。在JavaScript世界中,无论是在浏览器上还是在服务器上,都有很多活动。甚至还有为移动设备开发的javascript平台。好的。
随着我们的前进,我们中越来越多的人将编写异步代码,对此理解至关重要。好的。好啊。
注意,回调是一个单词。
维基百科的回调页面解释得很好。
引自维基百科页面:
In computer programming, a callback is a reference to executable code, or a piece of executable code, that is passed as an argument to other code. This allows a lower-level software layer to call a subroutine (or function) defined in a higher-level layer.
门外汉的回答是,在发生特定事件或处理某些代码后,用户或浏览器不会调用函数,而是调用函数。
回调函数是在满足某个条件时应该调用的函数。回调函数不是立即调用,而是在将来的某个点调用。
通常,当一个任务正在启动时,它将异步完成(即,在调用函数返回后将完成一段时间)。
例如,请求网页的函数可能要求调用方提供回调函数,当网页完成下载后将调用回调函数。
我相信这个"回调"术语在很多地方被错误地使用了。我的定义是:
A callback function is a function that you pass to someone and let
them call it at some point of time.
我认为人们刚刚阅读了wiki定义的第一句话:
a callback is a reference to executable code, or a piece of
executable code, that is passed as an argument to other code.
我已经使用了很多API,看到了各种不好的例子。许多人倾向于将函数指针(对可执行代码的引用)或匿名函数(一段可执行代码)命名为"回调",如果它们只是函数,为什么需要另一个名称?
实际上,只有wiki定义中的第二句话揭示了回调函数和普通函数之间的区别:
This allows a lower-level software layer to call a subroutine (or
function) defined in a higher-level layer.
所以区别在于你要传递函数的是谁,传递函数是如何被调用的。如果您只是定义一个函数并将其传递给另一个函数,并直接在该函数体中调用它,那么不要将其调用为回调。该定义表示传入函数将由"低级"函数调用。
我希望人们能停止在模棱两可的上下文中使用这个词,它不能帮助人们更好地理解,只是更糟。
回拨最容易用电话系统来描述。函数调用类似于打电话给某人,问她一个问题,得到一个答案,然后挂断电话;添加一个回调会改变这个类比,这样在问她一个问题之后,你也会给她你的名字和号码,这样她就可以用这个答案给你回电话。
——Paul Jakubik,"C++中的回调实现"
让我们保持简单。什么是回调函数?
寓言与类比举例
我有一个秘书。每天我都让她:(i)把公司的外发邮件寄到邮局,等她寄完后,再做:(i i)我在其中一张便签上为她写的任何任务。
现在,便签上的任务是什么?任务日复一日。
假设在这一天,我要求她打印一些文件。所以我把它写在便签上,然后把它和她要寄出的邮件一起钉在她的桌子上。
综上所述:
回调函数是第二个任务:打印这些文档。因为它是在邮件被丢弃之后完成的,而且还因为让她打印文档的便签和她需要邮寄的邮件一起给了她。
现在让我们把它与编程词汇联系起来。
- 在这种情况下,方法名为:DropofMail。
- 而回调函数是:printoffdocuments。printoffdocuments是一个回调函数,因为我们希望只有在Dropoffmail运行之后秘书才能这样做。
- 所以我将"pass:printoffdocuments"作为"参数"传递给dropoffmail方法。这一点很重要。
就这些。再也没有了。我希望你能把它弄清楚——如果没有,发表评论,我会尽力澄清。
这使得回调听起来像是方法末尾的返回语句。
我不确定它们是什么。
我认为回调实际上是对一个函数的调用,这是另一个函数被调用并完成的结果。
我还认为回调是为了处理原始调用,用一种"嘿!你要的那个东西?我已经做了-只是想让你知道-回到你身边"。
在后面打电话比用愚蠢的名字回叫要好。当或如果条件在一个函数中得到满足,则调用另一个函数,即在函数后调用,该函数作为参数接收。
与其硬编码函数内的内部函数,不如编写一个函数来接受函数后已经编写的调用作为参数。根据接收参数的函数中的代码检测到的状态更改,可能会调用后面的调用。
回调函数是您指定给现有函数/方法的函数,在操作完成时调用,需要额外处理等。
例如,在javascript中,或者更具体地说是jquery中,可以指定动画完成时要调用的回调参数。
在PHP中,
What is callback?
- 通常情况下,一种用来回电某人收到的电话。
- 在计算中,回调是作为参数传递给其他代码的一段可执行代码。当函数完成其工作(或发生某些事件)时,它会调用回调函数(它会回调您,因此会调用名称)。
What is a callback function?
- 回调函数就像一个仆人,当他完成一项任务时,他会"回叫"他的主人。
- 回调函数是作为参数传递给另一个函数(让我们调用另一个函数
otherFunction )的函数,回调函数在otherFunction 内被调用(或执行)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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中,回调允许插件模块从容器/环境访问服务。
类比:回调。异步的。非阻塞回调的现实例子
看图片:)。
主程序用回调函数名调用库函数(也可以是系统级函数)。此回调函数可能以多种方式实现。主程序根据需要选择一个回调。
最后,库函数在执行期间调用回调函数。
这个问题的简单答案是回调函数是通过函数指针调用的函数。如果将一个函数的指针(地址)作为参数传递给另一个函数,则当该指针用于调用该函数时,该指针指向另一个函数,即表示进行了回调。
假设我们有一个函数
我们可以选择任何算法,比如:
1 2 3 | 1. algorithmchosen = bubblesort 2. algorithmchosen = heapsort 3. algorithmchosen = mergesort ... |
比如说,原型已经实现了:
1 2 3 | 1. `void bubblesort(void)` 2. `void heapsort(void)` 3. `void mergesort(void)` ... |
这是一个用于实现面向对象编程中多态性的概念。
在计算机程序设计中,回调是对可执行代码的引用,或者是作为参数传递给其他代码的一段可执行代码。这允许较低级别的软件层调用在较高级别层中定义的子例程(或函数)。—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。好的。好啊。
回调函数是传递给某个函数或对象的函数(作为引用或指针)。此函数或对象将在以后的任何时候(可能多次)出于任何目的调用此函数:
- 通知任务结束
- 请求两个项目之间的比较(如在c qsort()中)
- 报告流程进度
- 通知事件
- 委托对象的陈述
- 授权绘制一个区域
…
因此,将回调描述为在另一个函数或任务末尾被调用的函数是过于简化的(即使它是一个常见的用例)。
回调函数,也称为高阶函数,是作为参数传递给另一个函数的函数,回调函数在父函数内部被调用(或执行)。
1 2 3 | $("#button_1").click(function() { alert("button 1 Clicked"); }); |
这里我们将函数作为参数传递给click方法。click方法将调用(或执行)我们传递给它的回调函数。
一个重要的使用领域是将函数之一注册为句柄(即回调),然后发送消息/调用某个函数来完成某些工作或处理。现在处理完成后,被调用的函数将调用我们注册的函数(即现在回调完成),从而指示我们的处理完成。这个维基百科链接很好地解释了图形。
回调函数作为参数传递给另一个函数的函数。
1 2 3 4 5 | function test_function(){ alert("Hello world"); } setTimeout(test_function, 2000); |
注意:在上面的示例中,测试函数用作setTimeout函数的参数。