每个JavaScript初学者至少都会面临这个问题:"什么是回调函数?"
嗯,我们可以从回调这个词本身找到答案。它就是在一个任务成功完成或失败后通知调用者。
在这篇文章中,我将不太关注回调的技术层面,而将尝试用自然语言解释它们是如何工作的。这应该可以帮助你理解什么是callback function ,以及为什么它的存在。
如果你是一个JavaScript初学者,那么这篇文章绝对适合你。
首先,什么是函数?
JavaScript中的函数是一组执行任务的语句。这组语句可以在没有函数的情况下存在,但把它们放在一个函数中可以帮助我们在多个地方重复使用这个任务。
下面是一个函数的例子,如果一个值是偶数,它就把这个值加倍。我们将一个数字作为参数传递给函数。函数中的语句检查参数是否是偶数。如果是,它就将其加倍并返回结果。否则,它返回原来的数字:
function doubleEven(n) {
if (n % 2 === 0) {
return n * 2;
}
return n;
}
现在你可以根据需要在很多地方使用这个函数:
doubleEven(10); // Output, 20
doubleEven(5); // Output, 5
你可以将一个函数作为参数传递给另一个函数
在上面的例子中,我们看到你可以把一个数字作为参数传给一个函数。同样,你也可以把一个函数作为参数传递。看看这个吧:
/**
Let's create a foo function that takes a
function as an argument. Here we invoke
the passed function bar inside foo's body.
*/
function foo(bar) {
bar();
}
好吧,那么我们现在如何调用foo呢?
/**
Invoke foo by passing a function as an argument.
*/
foo(function() {
console.log('bar');
}); // Output, bar
注意,我们把整个函数定义作为参数传给了foo 。传来的函数没有名字。它被称为anonymous function 。
什么是回调函数?
一个JavaScript函数接受另一个函数作为参数的能力是该语言的一个强大的方面。
函数的调用者可以把另一个函数作为参数传递给它,以便根据任何触发器来执行。让我们通过Robin and PizzaHub 的故事来了解它:

罗宾和必胜客的故事
罗宾,一个来自仙境的小男孩,喜欢吃披萨。一天早上,他拿起他妈妈的电话,用PizzaHub的应用程序订购比萨。罗宾选择了他最喜欢的奶酪烧烤披萨,并按下了订购按钮。
PizzaHub应用程序登记了订单,并通知罗宾,当比萨饼准备好并在路上时,它将notify 。罗宾,这个快乐的男孩,等了一会儿,终于收到了一个notification ,确认披萨已经在路上了!
所以,如果我们把这个故事拆开,事件的顺序是这样的:
- 罗宾
orders披萨 - 应用程序
notes down该订单 - PizzaHub
prepares披萨,过了一会儿就好了 - 应用程序
notifies罗宾,确认披萨已经在路上了
通知罗宾比萨的机制是通过使用callback 函数来实现的。
让我们用编程语言来写这个故事
是的,我们来做吧。上述事件的顺序是一组我们可以在逻辑上放在函数中的语句。
首先,Robin订购了比萨饼。应用程序通过调用一个函数来注册这个订单,就像这样:
orderPizza('Veg', 'Cheese Barbeque');
现在,生活在PizzaHub服务器某处的orderPizza() 函数可能会做其中的一些动作(实际上它可能做的比这多得多,但让我们保持简单):
function orderPizza(type, name) {
console.log('Pizza ordered...');
console.log('Pizza is for preparation');
setTimeout(function () {
let msg = `Your ${type} ${name} Pizza is ready! The total bill is $13`;
console.log(`On the Pizzahub server ${msg}`);
}, 3000);
}
setTimeout 函数展示了比萨饼的准备工作需要一些时间。在比萨饼准备好后,我们在控制台记录一条信息。然而,有一个问题!
消息被记录在PizzaHub ,而可怜的Robin对此没有任何线索。我们需要notify ,告诉他比萨饼已经准备好了。
引入一个回调函数
我们现在需要引入一个回调函数来让Robin知道披萨的状态。让我们改变orderPizza 函数,以传递一个回调函数作为参数。同时注意到,当比萨饼准备好时,我们要用消息来调用callback 函数:
function orderPizza(type, name, callback) {
console.log('Pizza ordered...');
console.log('Pizza is for preparation');
setTimeout(function () {
let msg = `Your ${type} ${name} Pizza is ready! The total bill is $13`;
callback(msg);
}, 3000);
}
现在,让我们对orderPizza 函数的调用进行修改:
orderPizza('Veg', 'Cheese Barbeque', function(message){
console.log(message);
});
所以现在一旦比萨饼准备好了,调用者将使用回调函数得到通知。这不是很有用吗?
总结一下
总结一下:
- 一个JavaScript函数可以接受另一个函数作为参数
- 将函数作为参数传递是一个强大的编程概念,可以用来通知调用者发生的事情。它也被称为回调函数
- 你可以根据一个用例使用回调函数来通知调用者。回调也被用来根据其他任务的状态(通过、失败)来执行某些任务
- 但是要小心--嵌套太多的回调函数可能不是一个好主意,可能会产生
Callback Hell。我们将在接下来的文章中了解更多这方面的信息