# 什么是回调?

「简单说」:回调是一个要在另一个函数执行完毕后执行的函数,因此称为回调。

「复杂的说」:在 Javascript 中,函数是一个对象。因此,函数可以将一个函数作为参数,并且可以由其他函数返回。这样的函数我们一般称为高阶函数 「higher-order functions」。任何被作为参数传递的函数都可以称为回调函数。

百说不如一练,让我们用几个例子来进一步分析这个问题。

# 为什么我们需要回调?

其中一个很重要的原因是:Javascript 是一种事件驱动的语言。这意味着 Javascript 将在监听其他事件同时继续执行,而不是等待其响应。让我们看看下面的例子:

function first() {
  console.log(1)
}
function second() {
  console.log(2)
}
first()
second()

正如我们所预料的,首先执行的是 「first」 函数,然后才是 「second」 函数执行。控制台中打印结果如下:

// 1
// 2

目前来看,一切都很好。

但是当 「first」 函数如果包涵一些不能立即执行的代码呢?比如说,我们必须在其中发送一个请求然后等待其响应结果。我们可以使用 「setTimeout」 来模拟这个操作,setTimeout 是 Javascript 中可以设置在延迟一段时间执行的函数的函数。我们将使用 setTimeout 函数延迟 500ms 来模拟 API 请求。代码如下:

function first() {
  setTimeout(function() {
    console.log(1)
  }, 500)
}
function second() {
  console.log(2)
}
first()
second()

现在不了解 setTimeout 如何运行并不重要。重要的是我们看到 console.log (1) 的位置改变了;它会在 500ms 延迟后执行。那么让我们看看调用函数时发生了什么?

first()
second()
// 2
// 1

虽然我们首先调用了 first 函数,但是它却在 second 函数输出后得到输出结果。

这并不是说 Javascript 没有按照我们想要的执行顺序来执行我们的函数,而是 「Javascript 在执行 seconde () 函数时没有等待 first () 的响应」

之所以展示上述的问题,是因为我们不能一个函数接着一个函数的调用并期望它们按照正确的顺序执行。「回调函数是一种确保代码不会在其他代码完全执行完成后执行的方法」

# 创建一个回调

下面让我们创建一个回调函数!

打开我们的浏览器控制台(Windows:Ctrl + Shift + J)(Mac: Cmd + Option + J),并且输入以下函数声明:

function doHomework(subject) {
  alert(`Starting my ${subject} homework`);
}

如上代码,我们创建了 doHomework 函数。我们的函数接收一个变量,既是我们正在研究的 subject。可以在控制台输入以下代码执行 doHomework 函数:

doHomework('math');
// alert: Starting my math homework

下面让我们添加回调函数 - 作为 doHomework 函数的最后一个参数。我们可以向其中传入 callback 然后编写回调函数:

function doHomework(subject, callback) {
  alert(`Starting my ${subject} homework.`);
  callback();
}
doHomework('math', function() {
  alert('Finished my homework');
});

上述代码可以看到,如果你在控制台上输入上述代码,你可将会得到两个弹窗。

但是,我们并不需要一定要在函数中定义回调函数。我们可以在任何位置定义它,代码如下:

function doHomework(subject, callback) {
  alert(`Starting my ${subject} homework.`);
  callback();
}
function alertFinished(){
  alert('Finished my homework');
}
doHomework('math', alertFinished);

上述例子得到的结果和之前的例子的结果完全相同,但是实现的方式却略显不同。正如我们所见的,我们在 doHomework () 函数调用期间将 alertFinished 作为参数传入其中。

# 结束语

通过上述这些例子,想必你已经可以理解什么是回调和其是怎样工作的。然而这些只是回调的冰山一角,还有很多东西值得我们学习。