什么是回调函数?


当前回答

开发人员常常因为该死的东西的名称而被什么是回调弄糊涂。

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

可由其他功能访问,以及在第一个函数完成后调用

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

也许一个更好的名称是“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.

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

回调是所谓的,因为它们与指针语言一起使用。如果你不使用其中一个,就不要为“回调”这个名字而烦恼。只需理解,它只是一个名称,用来描述作为另一个方法的参数提供的方法,这样当调用父方法(无论什么条件,如按钮单击、计时器滴答声等)且其方法体完成时,就会调用回调函数。

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

其他回答

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

我在这个问题上已经迟到了13年,但在我自己学习之后,我想我应该在这里再加一个答案,以防有人像我一样困惑。

其他答案总结了“回调是什么?”这一问题的症结所在

它只是一个函数,在完成某个任务时调用另一个函数。

让我感动的是这样的例子:“你做了这个,现在做了那个。”比如,当我自己可以调用一个方法或函数时,为什么我会这样使用它?

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

超伪码

首先,你会遇到的核心问题。。。。

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%不是实现它的最佳方式,我只是想给出一个明确的例子。

所以这并不是简单的“回调是什么?”它是“回调是什么,它有什么好处???”

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

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

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

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

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

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

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

使用函数指针在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)();                               
}

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

这是一个演示函数回调的程序内部寄存器回调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/Oobserver中,被调用者可以通知对该事件感兴趣的任何人(通知可能会发送到应用程序中未触发任务的其他部分)

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

事件界面

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.exampleventlistener;

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和OnLongClickEventListen

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

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