文档结构  
翻译进度:已翻译     翻译赏金:10 元 (?)    ¥ 我要打赏

学习 JavaScript 的时候,很快就会遇到回调。对于初学者来说,它们显得陌生,充满神秘,然而为了驾驭语言的力量,搞懂这个问题就显得非常重要了。我会在这篇文章中通过简单易必的例子教你关于回调的基本知识。

回调 —图片来自 unsplash

什么是回调?

简单的说:回调是一个函数,它会在另一个函数(通常是异步的)执行完成之后才会被调用执行——因此叫做“回调”。

更复杂的解释:JavaScript 中,函数也是对象。正因为如此,函数可以作为函数的参数,也可以从另一个函数返回出来。拥有这种特性的函数称为高阶函数。任何作为参数传入,并由接受它的函数来进行调用的函数,被称为回调函数。

第 1 段(可获 1.75 积分)

说了这么多,我们还是来通过一些例子来实际说明一下。

本文最初发表在 codeburst.io 现在又经过作者允许发表在这里。假如你喜欢阅读,你可以直接看看作者的其他文章 -> 《Brandon 的其他文章》? 如果你想提高一下Javascript水平的话, 也可直接参加我们的高级培训《Javascript课程介绍》.

为什么需要回调?

一个非常重要的原因就是 — JavaScript 是事件驱动语言. 这意味着,在继续运行之前,JavaScript不会继续等待响应,而是在侦听其他事件时继续执行。我们来看一个简单示例:

第 2 段(可获 1.54 积分)
function first(){
  console.log(1);
}

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

first();
second();

如你所愿,first()函数首先执行,接着说second()函数被执行 — 控制台打印了如下信息:

// 1
// 2

目前为止一切都没问题。

但是如果first()函数包含一些不能立即执行的代码呢?比如说API请求, API请求时要发送请求然后等待响应的。为了模拟这种场景, 我们使用Javascript的本地方法 setTimeout 做一个特定时间的延迟模拟。我们使用500毫秒的时间来模拟一次API请求。现在代码看来是这样的:

第 3 段(可获 1.28 积分)
function first(){
  // Simulate a code delay
  setTimeout( function(){
    console.log(1);
  }, 500 );
}

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

first();
second();

对你来说,现在要不要理解 setTimeout() 的工作原理并不重要(如果你比较好奇,可以阅读这篇文章 a tutorial on that very subject)。你所需了解的是我们的 console.log(1); 语句,这个语句延迟 500 毫秒执行。因此,如果调用我们的函数会怎样呢?

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

虽然我们首先调用了 first() 函数,我们在 second() 函数之后记录了这个函数的结果。

第 4 段(可获 0.85 积分)

并不是Javascript不按照我们预想的方式执行程序,而是Javascript并不会在执行first()方法的时候进行等待,而是继续执行了second()方法。

为什么要看这段代码呢?你不能单纯的以为会先调用一个函数再调用另外一个,他们就会按调用先后顺序执行。回调就是用来确保某些特定的代码只有在另外一些代码执行完成之后再执行的一种方法。

创建回调

说了那么多,还是来点实际的吧!

首先,打开浏览器开发者控制台 F12 (Windows/Linux 按 Ctrl + Shift + J , Mac 按Cmd + Option + J ) 。然后在控制台输入下面的函数:

第 5 段(可获 1.5 积分)
function doHomework(subject) {
  alert(`Starting my ${subject} homework.`);
}

上面的代码中我们创建了一个名为 doHomework 的函数。这个函数接受一个参数:subject。接着在控制台敲入以下代码调用这个函数:

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');
});
第 6 段(可获 0.75 积分)

在 JS Bin 上尝试运行这段示例代码

JS Bin on jsbin.com

如你所见,在浏览器控制台敲入以上代码执行后会弹出两个对话框:首先弹出 ‘Starting homework’,然后弹出 ‘Finished homework’ 。

不过回调函数并不是必须定义在调用其他函数的地方。你可以定义在任何位置就像这样:

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

function alertFinished(){
  alert('Finished my homework');
}

doHomework('math', alertFinished);

在JS Bin 上运行这个代码示例:

第 7 段(可获 0.9 积分)

jsbin.com 上 关于回调的解释

这个示例的运行结果与上一个是一模一样的,只是代码编写方式稍微有点不同。如你所见,我们在调用  doHomework() 函数时把alertFinished 函数的定义作为参数进行了传递!

实际应用场景

上周我发布了一篇名为 《怎样用38行代码创建一个Twitter机器人》 的文章。 为什么我这么牛逼,因为我直接使用了Twitter的API。当请求API时,你必须等待请求结果才能继续做后面的事情。下面是一个实际场景中使用回掉的典型示例。来看下代码:

第 8 段(可获 1.53 积分)
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 请求。
  • 这个请求中存在 3 个参数:‘search/tweets’ 是这个请求的路径,params 是搜索参数,最后的匿名函数就是回调。

这里的回调非常重要,因为我们需要等到服务器响应之后再执行其它代码。我们并不清楚对 API 的请求是否会成功,所以在向 search/tweets 发送参数进行 GET 请求之后,需要等待。一旦 Twitter 发回响应,回调函数就会执行。Twitter 可能会发回 err (错误) 对象,也可能发回响应对象。在我们的回调函数中,我们可以使用 if() 语句来检查请求是否成功,如果成功则继续处理新数据。

 

第 9 段(可获 1.7 积分)

你已经懂了

非常好!你现在已经知道什么是回调,以及它如何运作。但这只是回调相关知识的冰山一角,还有很多东西可以学!如果你有疑问或者什么想法,非常欢迎你在下面的评论区发表评论。

 

第 10 段(可获 0.69 积分)

文章评论