回调函数

什么是回调呢?比如我们在编写 JavaScript 脚本时,不知道用户何时点击按钮,因此通常会为按钮的点击事件定义一个事件处理程序,该事件处理程序会接收一个函数,用来在点击事件被触发时调用,这就是所谓的回调。

因此,回调本质上是一个函数,它可以作为值传递给另一个函数,并且只有在特定事件发生时才会被执行。例如,下面代码给按钮的点击事件绑定一个回调函数:

document.getElementById('button').addEventListener('click', () => {
     …                       //被点击
})

Node.js 异步编程的直接体现就是回调函数,Node.js 中使用了大量的回调函数,Node.js 中的大部分 API 都支持回调函数,如第 7 章中讲解过的操作文件的方法,基本上都同时提供了同步和异步操作的方法,并且默认使用的都是异步操作,在异步操作方法中都需要传递一个 callback 回调函数。

【例9.1】回调函数的简单应用。(实例位置:资源包\源码\09\01)

WebStorm 中创建一个 .js 文件,其中定义两个函数 fooAfooB,然后将 fooA 函数作为参数传递给 fooB,以执行相应的加法运算,代码如下:

function  fooA() {
    return 1
}
function  fooB(a) {
    return 2 + a
}
//fooA是一个函数,但这里作为一个参数在fooB函数中被调用
c = fooB(fooA())
console.log(c)

运行程序,结果如图9.5所示。

image 2024 04 13 20 37 14 996
Figure 1. 图9.5 回调函数的简单应用

接下来,通过一个实例讲解如何通过异步编程调用回调函数。

【例9.2】异步调用回调函数。(实例位置:资源包\源码\09\02)

WebStorm 中创建一个 .js 文件,该文件中主要实现将一个全局变量 a 从初始值 0 在 3 秒后变为 6 的过程,代码如下:

var a = 0;
function fooA(x) {
     console.log(x)
}
function timer(time) {
     setTimeout(function () {
          a=6
     }, time);
}
console.log(a);
timer(3000);
fooA(a);

运行程序,效果如图9.6所示。

通过观察图9.6可以发现,程序并没有按照我们的设想执行,这是因为虽然 timer 函数中将变量 a 设置为 6,但是程序执行时,由于 timer 函数中使用了 setTimeout,其不会阻塞后面代码的执行,因此程序并不会等待 timer 函数执行完,而是直接执行了最后一行的 fooA 函数,而此时还没有经过 3 秒的时间,所以 a 的值仍是 0。

image 2024 04 13 20 39 59 028
Figure 2. 图9.6 异步调用回调函数(1)

如果想达到我们希望的效果,应该在 timer 函数中加入一个回调函数作为参数,然后调用时,将 fooA 函数作为参数传递给 timer 函数,即代码修改如下:

var a = 0
function fooA(x) {
     console.log(x)
}
function timer(time, callback) {
     setTimeout(function () {
          a = 6
          callback(a);            //使用回调函数执行输出操作
     }, time);
}
console.log(a)
timer(3000,fooA)

再次运行程序,结果如图9.7所示。

image 2024 04 13 20 41 01 090
Figure 3. 图9.7 异步调用回调函数(2)