原文链接:https://www.sitepoint.com/callbacks-javascript/
当我们刚开始学习JavaScript的时候,很快就会碰到回调函数。这对初学者来说,回调函数既陌生又神秘。然而掌握它的原理,是掌握(JavaScript)这门语言的关键之一。在这篇文章中,通过简单易懂的例子,希望能让你掌握回调函数的基础。
电话(译者注:这个真是一语相关,原文是Callbacks,指回拨的电话号码之类,也是回调的一种) — 图片由unsplash提供
什么是回调?
简单地说: 一个回调函数,就是在另外一个函数(通常是异步的)执行完之后再执行的函数,因而被命名为——回调。
更进一步地说: 在JavaScript中,函数是对象。正因如此,一个函数可以被其他函数作为参数(传入),也能被其他函数作为返回值返回。这种函数(译者注:起码要满足如下条件之一:1.接受一个或多个函数作为参数,2.将一个函数作为返回值返回)被称为高阶函数。任何函数,只要它作为参数传入且随后被调用,都可称之为回调函数。
看起来很复杂的样子,但别急,让我们往下看一些例子。
这篇文章首发于codeburst.io且获得了作者授权转载。如果你喜欢阅读, 为什么不去看看Brandon的其他文章呢?如果你想提高你的JavaScript技巧,可以前往SitePoint Premium参加我们的JavaScript入门课程。
为什么需要回调函数?
这是由于一个十分重要的原因 —— JavaScript是一门事件驱动的语言。这就意味着,JavaScript会持续监听事件且一直往下执行,而不会在事件响应前一直挂起。让我们一起看看下面这个简单的例子:
1 | function first(){ |
正如你所料,函数first会首先执行,函数second会在之后执行——控制台会打印如下结果:
1 | // 1 |
看起来是符合我们的预期。
但如果函数first包含有一些不会立即执行的代码呢?例如,我们需要发送一个请求并等待它的响应呢?我们使用setTimeout
去模拟这种场景,setTimeout
是原生JavaScript方法,允许你在特定时间后调用一个函数。我们通过延迟500毫秒后再执行函数来模拟发送请求。代码如下:
1 | function first(){ |
你现在不太需要立即搞懂setTimeout()
的原理(如果你很好奇,我们有这主题相关的文章)。(现在)最重要的是我们将console.log(1);
延迟500毫秒执行。那么,当我们执行这两个函数(first
和second
)时,会发生什么呢?
1 | first(); |
尽管我们先调用函数first,但会先打印出2
。
这并不是JavaScript不按照我们的想法去顺序执行函数,而是JavaScript会继续执行函数second,而不是等待函数fitst执行完(再去执行函数second)。
那么为何让你看这个(例子)呢?是因为你不能期望Javascript都是执行完一个函数后再去执行下一个函数。而回调函数是确保代码按次序正确执行的一种方法。
创造一个回调函数
好了,让我们少啰嗦,一起来创造一个回调函数吧!
首先,打开你浏览器的控制台(Windows/Linux和系统可以按下Ctrl + Shift + J,Mac则按下Cmd + Option + J)。然后把以下的函数输入到控制台内:
1 | function doHomework(subject) { |
我们创建了一个名为doHomework
的函数。而我们的函数需要传入一个变量才能工作。可以往控制台中输入以下代码调用(该函数)。
1 | doHomework('math'); |
现在我们把回调函数添加进去吧。doHomework
需要添加多一个参数,回调函数会作为最后一个参数传入,并在执行doHomework
时调用。
1 | function doHomework(subject, callback) { |
试着在JS Bin上执行上述代码
正如你所看到的,当你在控制台输入上述代码后,陆续会有两个告示框弹出:首先是:‘Starting homework’,然后是‘Finished homework’。
然而回调函数不一定要在函数调用时完整传入。你可以将回调函数定义在其他地方,如以下代码所示:
1 | function doHomework(subject, callback) { |
在JS Bin上执行上述例子
执行的结果与之前的例子是完全一致的,但代码的组织却有一点不同。正如你所见,在doHomework
函数调用时,alertFinished
作为它的一个参数被传入!
一个真实的例子
上星期,我发布了一篇名为《如何用38行代码创造一个推特机器人》的文章。那篇文章中,代码是通过使用推特的API来工作的。当你发请求去一个API时,你必须等待响应内容返回后才能继续工作。这是一个说明如何使用回调函数的很好例子。请求API时发送的请求代码如下:
1 | T.get('search/tweets', params, function(err, data, response) { |
T.get
的意思只是往推特发送一个GET请求。当发送请求去
‘search/tweets’
时,(请求函数)接受三个参数,第一个参数是请求地址,params
是第二个参数,代表搜索相关的参数,而最后一个则是一个匿名的回调函数。
在这里,回调函数是十分重要的,因为我们需要等待服务器返回数据后,才能继续执行余下代码。我们并不知道我们请求API后,服务器是否成功地返回数据。因此,当我们通过GET请求请求search/tweets
后,会等待服务器返回信息。一旦推特(的服务器)返回相应数据,我们的回调函数将会执行。推特服务器会返回一个err
(error) 对象或一个response
对象给我们。在我们的回调函数中,我们使用条件判断语句if()
(判断是否有错误对象存在)来确定请求是否成功,(判断成功)之后再根据返回的数据进行下一步操作。
初战告捷
干得漂亮!你现在应该初步理解回调函数是什么和它是如何工作的了,但这只是(回调函数知识中)的冰山一角,你仍有很多东西需要学习!如果你有任何问题或想法,请在评论中告诉我,我很乐意与你们分享和交流。