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


当前回答

程序员Johny需要一个订书机,所以他去办公用品部门要了一个,填写完申请表后,他可以站在那里等着店员去仓库里找订书机(就像一个阻塞函数调用),或者去做其他的事情。

由于这通常需要时间,johny在申请表格上写了一张便条,要求他们在订书机准备好取书时给他打电话,这样他就可以去做其他事情,比如在办公桌上打盹。

其他回答

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

First show your students how to use and manipulate variables using pointers alongside using the normal variable identifiers. Then teach them there are things that can be done only with pointers(like passing a variable by reference). Then tell them how executable code or functions are just like some other data(or variables) in the memory. So, functions also have addresses or pointers. Then show them how functions can be called with function pointers and tell these are called callbacks. Now, the question is, why all these hassle for calling some functions? What is the benefit? Like data pointers, function pointer aka callbacks has some advantages over using normal identifiers. The first one is, function identifiers or function names cannot be used as normal data. I mean, you cannot make a data structure with functions(like an array or a linked list of functions). But with callbacks, you can make an array, a linked list or use them with other data like in dictionary of key-value pairs or trees, or any other things. This is a powerful benefit. And other benefits are actually child of this one. The most common use of callbacks is seen in event driver programming. Where one or more functions are executed based on some incoming signal. With callbacks, a dictionary can be maintained to map signals with callbacks. Then the input signal resolution and execution of corresponding code become much easier. The second use of callbacks coming in my mind is higher order functions. The functions which takes other functions as input arguments. And to send functions as arguments, we need callbacks. An example can be a function which take an array and a callback. Then it performs the callback on each of the item of the array and return the results in another array. If we pass the function a doubling callback, we get a doubled valued array. If we pass a squaring callback, we get squares. For square roots, just send appropriate callback. This cannot be done with normal functions.

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

我要尽量让这个问题简单化。“回调”是由另一个以第一个函数为参数的函数调用的任何函数。很多时候,“回调”是在发生某些事情时调用的函数。在编程语言中,这可以被称为“事件”。

想象一下这样的场景:你几天后就会收到一个包裹。这个包裹是给邻居的礼物。因此,一旦你得到了包裹,你想把它带到邻居那里。你出城了,所以你给你的配偶留下了指示。

你可以让他们去拿包裹,然后送到邻居那里。如果你的配偶像电脑一样愚蠢,他们会坐在门口等包裹来(什么也不做),然后一旦它来了,他们就会把它带到邻居那里。但是有一个更好的办法。告诉你的配偶,一旦他们收到包裹,他们应该把它带到邻居那里。然后,他们可以正常生活,直到他们收到包裹。

在我们的例子中,包的接收是“事件”,而把它带给邻居是“回调”。你的配偶“执行”你的指示,只有当包裹到达时才把包裹带过来。更好的!

这种思维在日常生活中很明显,但计算机没有这种常识。考虑程序员通常如何写入文件:

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

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

# 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来获得一些这种思考的实际实践。

回调函数是作为参数传递给另一个函数的函数(在某些时候使用)。

以下是一些函数:

def greeting(name):
    print("Hello " + name + "!")

def departing(name):
    print("Goodbye " + name + "!")

下面是一个函数(使用ourCallBack作为回调参数):

def promptForName(ourCallback):
    myName = input("Enter Name:")
    ourCallback(myName)

现在让我们使用一些回调!

promptForName(greeting) 
# Enter Name: 
# >Ed
# Hello Ed!

promptForName(departing) 
# Enter Name: 
# >Ed
# Goodbye Ed!

promptForName(greeting) 
# Enter Name: 
# >Guy
# Hello Guy!

我能够很快地扩展我的代码。


处理(错误和误导性的)答案:

回调并不意味着异步!

JS在2015年得到承诺,async/await在2017年得到承诺。在此之前,使用回调。

这就是为什么这里的一些答案没有意义,他们把两者混为一谈了!

它们通常用于异步代码,但我的示例是同步的。

回调并不意味着事件驱动!

它们通常用于事件处理,但我的示例不是事件。

回调并不意味着闭包!

虽然通常用作提供闭包的一种简洁方式,但我的示例并不是这样。

回调不是第一类函数的完整定义!

它是创建第一类函数定义的众多特性之一。

C语言可以使用函数指针作为回调函数,尽管它没有第一类函数。

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

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

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

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

通常,我们将变量发送给函数:function1(var1, var2)。

假设,你想在它被作为参数给出之前处理它:

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

编辑:回调这个词最常见的意思是一个函数作为参数传递给另一个函数,并在稍后的时间点被调用。这些思想存在于允许高阶函数的语言中,即将函数视为一等公民,通常用于异步编程中。onready dosomething()。在这里,只有当它准备好了,事情才会发生。