如何用简单的英语解释回调?它们与从一个函数调用另一个函数从调用函数获取上下文有什么不同?如何向新手程序员解释它们的强大功能?


当前回答

当我们有两个函数,函数a和函数b,如果函数a依赖于函数b。

然后我们调用函数b作为回调函数。这在Spring框架中被广泛使用。

其他回答

从一个例子开始总是更好的:)。

假设有两个模块A和B。

你希望模块A在模块B中发生某些事件/条件时得到通知。然而,模块B对模块A一无所知。它只知道模块A提供给它的函数指针指向模块A的特定函数的地址。

因此,所有B现在必须做的是,当一个特定的事件/条件发生时,使用函数指针“回调”到模块A。A可以在回调函数内部进行进一步处理。

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

回调函数:

我们定义了一个名为callback的回调函数,给它一个参数otherFunction,并在函数体中调用它。

function callback(otherFunction){
    otherFunction();
}

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

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

烤披萨的例子。 烤炉 披萨底,上面有配料 这里,烤箱是回调函数。 披萨的配料是另一个功能。

值得注意的是,不同的披萨原料可以做出不同类型的披萨,但烘焙披萨的烤箱是一样的。 这在某种程度上是回调函数的工作,它不断期望具有不同功能的函数,以产生不同的自定义结果。

让我们假设您要给我一个可能长期运行的任务:获取您遇到的前五个独特的人的名字。如果我在人口稀少的地区,这可能需要几天的时间。你真的不想在我忙来忙去的时候袖手旁观,所以你说:“你拿到名单后,打我手机给我读一遍。这是号码。”

您已经给了我一个回调引用——一个我应该执行以传递进一步处理的函数。

在JavaScript中,它可能是这样的:

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);

这可能有很多方面可以改进。例如,你可以提供第二次回拨:如果最终超过一个小时,打电话给红色电话,告诉接听电话的人你超时了。

将方法看作是将任务交给同事。一个简单的任务可能如下:

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

你的同事勤奋地计算了一下,并给出了以下结果:

x = -6
y = -4

但是你的同事有一个问题,他并不总是理解符号,比如^,但是他通过它们的描述理解它们。如指数。每次他找到其中一个,你就会得到以下信息:

I don't understand "^"

这就要求你在向你的同事解释完这个角色的含义后,再重写一遍你的整个指令集,而他并不总是在问题之间记得。而且他也很难记住你的建议,比如直接问我。然而,他总是尽可能地遵循你的书面指示。

你想到了一个解决方案,你只需在所有的指令中添加以下内容:

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

现在,无论何时他有问题,他都会打电话问你,而不是给你一个糟糕的回复,让整个过程重新开始。

想象一下,一个朋友要离开你的家,你告诉她“到家后给我打个电话,好让我知道你已经安全到家了”;这是(字面上的)回电。这就是回调函数,与语言无关。您希望某个过程在完成某些任务后将控制传递回给您,因此您可以给它一个函数,用于回调。

例如,在Python中,

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

grabDBValue可以被编写为只从数据库获取一个值,然后让您指定对该值实际做什么,因此它接受一个函数。您不知道grabDBValue何时或是否会返回,但是如果/当它返回时,您知道希望它做什么。在这里,我传入一个匿名函数(或lambda),它将值发送到GUI窗口。我可以通过这样做轻松地改变程序的行为:

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

回调在函数是第一类值的语言中工作得很好,就像通常的整数、字符串、布尔值等。在C语言中,你可以通过传递指向函数的指针来“传递”函数,调用者可以使用它;在Java中,调用者将请求具有特定方法名的特定类型的静态类,因为类之外没有函数(实际上是“方法”);在大多数其他动态语言中,您可以通过简单的语法传递函数。

Protip:

在具有词法作用域的语言(如Scheme或Perl)中,您可以使用这样的技巧:

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,因为回调可以访问定义它的词法环境中声明的变量。词法作用域和匿名回调是一个强大的组合,值得新手进一步研究。