Fabrício 的回答非常正确;但我想用一些不太技术性的东西来补充他的回答,重点是通过类比来帮助解释异步性的概念
打个比方……
昨天,我工作中需要一位同事提供一些信息。我给他打了电话;以下是通话内容:
我 :嗨,鲍勃,我想知道我们 在 酒吧 做 。吉姆想要一份报告,而你是唯一一个知道细节的人。
鲍勃 :当然可以,但是要花大约 30 分钟的时间吧?
我 :太好了,鲍勃。等你了解情况后再给我打电话吧!
这时,我挂断了电话。由于我需要 Bob 提供的信息来完成我的报告,所以我放下报告去喝了杯咖啡,然后查看了一些电子邮件。40 分钟后(Bob 很慢),Bob 回电并给了我所需的信息。此时,我又继续写报告,因为我已经掌握了所需的所有信息。
想象一下如果对话是这样的;
我 :嗨,鲍勃,我想知道我们 在 酒吧 做 。吉姆想要一份报告,而你是唯一一个知道细节的人。
鲍勃 :当然可以,但是要花大约 30 分钟的时间吧?
我 :太好了,鲍勃。我会等的。
我坐在那里等着。等了又等。等了 40 分钟。什么也没做,只是等待。最后,鲍勃给了我信息,我们挂了电话,我完成了报告。但我损失了 40 分钟的生产力。
这是异步与同步行为
这正是我们问题中所有示例中的情况。加载图像、从磁盘加载文件以及通过 AJAX 请求页面都是缓慢的操作(在现代计算环境中)。
JavaScript 允许您注册一个回调函数,该函数将在慢速操作完成后执行, 而不是 等待 其他代码 使行为变得 异步 。如果 JavaScript 等待操作完成后再执行任何其他代码,这将是 同步 行为。
var outerScopeVar;
var img = document.createElement('img');
// Here we register the callback function.
img.onload = function() {
// Code within this function will be executed once the image has loaded.
outerScopeVar = this.width;
};
// But, while the image is loading, JavaScript continues executing, and
// processes the following lines of JavaScript.
img.src = 'lolcat.png';
alert(outerScopeVar);
在上面的代码中,我们要求 JavaScript 加载 lolcat.png
,这是一个 很慢的 操作。回调函数将在这个缓慢的操作完成后执行,但与此同时,JavaScript 将继续处理下一行代码;即 alert(outerScopeVar)
.
这就是我们看到显示警报的原因 undefined
;因为是 alert()
立即处理的,而不是在图像加载之后处理的。
为了修复我们的代码,我们要做的就是将<代码> alert(outerScopeVar) code 中 。因此,我们不再需要将 outerScopeVar
变量声明为全局变量。
var img = document.createElement('img');
img.onload = function() {
var localScopeVar = this.width;
alert(localScopeVar);
};
img.src = 'lolcat.png';
您 总是 看到回调被指定为一个函数,因为这是 JavaScript 中定义某些代码但稍后才执行的唯一方法。
因此,在我们所有的示例中,都是 function() { /* Do something */ }
回调;要修复 所有 示例,我们所要做的就是将需要操作响应的代码移到那里!
* Technically you can use eval()
as well, but eval()
对于此目的来说是邪恶的 for this purpose
如何让呼叫者等待?
您目前可能有一些类似于此的代码;
function getWidthOfImage(src) {
var outerScopeVar;
var img = document.createElement('img');
img.onload = function() {
outerScopeVar = this.width;
};
img.src = src;
return outerScopeVar;
}
var width = getWidthOfImage('lolcat.png');
alert(width);
但是,我们现在知道 会 return outerScopeVar
立即发生;在 onload
回调函数更新变量之前。这会导致 getWidthOfImage()
返回 undefined
,并 undefined
发出警报。
为了解决这个问题,我们需要允许函数调用 getWidthOfImage()
注册一个回调,然后将宽度的警报移动到该回调内;
function getWidthOfImage(src, cb) {
var img = document.createElement('img');
img.onload = function() {
cb(this.width);
};
img.src = src;
}
getWidthOfImage('lolcat.png', function (width) {
alert(width);
});
...和以前一样,请注意我们已经能够删除全局变量(在本例中 width
)。