JavaScript:什么是回调?

翻译练习

原博客地址:JavaScript: What the heck is a Callback?

在6分钟内通过简单的例子学习和理解回调的基本原理。

什么是回调?

简单地说:回调就是一个在另一个函数执行完成后再去执行的函数--因此得名回调。

复杂点讲:在JavaScript中,函数是对象。因此,函数可以把其他函数当做参数,也可以被其他函数返回。这样做的函数称为高阶函数。任何被当做参数传递的函数都叫回调函数。

上面已经讲了很多,让我们通过一些例子把这些去细化一下。

为什么我们需要回调?

有一条非常重要的原因:JavaScript是一门事件驱动的语言。这意味着,JavaScript在监听其他事件的同时会继续执行,而不是在继续执行之前去等待响应。

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

正如你期望的那样,函数first会首先执行,函数second会接着执行--控制台打印如下:

// 1
// 2

目前看来一切良好。

但是,如果函数first包含一些不会立即执行的代码会发生什么呢?比如说,一个API请求,我们必须去发送请求然后等待响应吗?为了模拟这种动作,我们使用setTimeout--在JavaScript中,这个函数将会在设定的一段时间后去调用一个函数。我们来把我们的函数延迟500ms去模拟一个API请求。我们的新代码像下面这样:

function first(){
  // Simulate a code delay
  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在执行second()函数之前并没有等待first()函数的响应。

所以,为什么要给你展示这些呢?因为你不能一个接一个的去调用函数,然后希望它们以正确的顺序去执行。回调是一种能够保证某些代码直到一些代码执行完才去触发的方式。

创建一个回调

好吧,说的够多的了,让我们来创建一个回调。

首先,打开你的Chrome开发者控制台(Windows: Ctrl + Shift + J)(Mac: Cmd + Option + J),然后在控制台输入下面的函数声明:

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

在上面,我们创建了一个叫做doHomework 的函数。我们的参数接受一个参数--我们正在学习的课程。通过在控制台输入以下代码来调用你的函数:

doHomework('math');
// Alerts: Starting my math homework.

现在,让我们来添加回调--作为doHomework()函数的最后一个参数,我们可以传入回调。随后,在我们调用doHomework()时,回调函数被定义为它的第二个参数。

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

doHomework('math', function() {
  alert('Finished my homework');
});

如你所见,如果你在控制台输入上面的代码,你会连续收到两次警告:先是“starting homework”,再是“finished homework”。

但是回调函数并不是必须在我们的函数调用中定义,它们可以向下面这样在其他地方被定义:

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

这个例子的结果和上一个的一模一样,但是设置却有一点区别。我们在调用doHomework()的时候,把函数定义alertFinished 作为参数传给了它。

现实世界的例子

上周我发布了一篇文章,这篇文章中代码运行的唯一原因就是Twitters API。当你对一个API发起请求时,你必须等到它响应才能对相应进行操作。这是一个现实生活中关于回调的完美例子。下面是这个请求的代码:

T.get('search/tweets', params, function(err, data, response) {
  if(!err){
    // This is where the magic will happen
  } else {
    console.log(err);
  }
})
  • T.get仅仅表示我们在向Twitter发起一个get请求。
  • search/tweets请求中有三个参数,分别是:请求的路由、查询的参数param和我们的回调--一个匿名函数。

回调在这里很重要,因为在运行到后面的代码之前,我们需要先从服务器获取到响应。我们不知道在我们讲参数通过get请求发送到search/tweets 后我们的API请求成功与否,我们只能等着。一旦Twitter做出响应,我们的回调函数就会被调用。Twitter要么返回一个错误信息给我们,或者返回响应对象给我们。在我们的回调函数中,我们可以通过if()语句去确定我们的请求是成功了还是失败了,然后相应地对新数据做出处理。

你做到了

干得不错。理想情况下,你现在已经理解了什么是回调还有它是怎么工作的。这仅仅是回调的冰山一角,还有很多你需要去学习。我每周会发布了一些文章/教程,如果你想被加入到我的once-weekly邮箱列表,在这里了输入你的邮箱吧。

posted @ 2020-12-15 20:43  rain_watcher  阅读(328)  评论(0编辑  收藏  举报