Fabrício的答案是正确的;但我想用一些不那么技术性的东西来补充他的回答,侧重于一个类比来帮助解释异步性的概念。
一个类比……
昨天,我正在做的工作需要从一位同事那里得到一些信息。我给他打了电话;对话是这样进行的:
我:嗨,鲍勃,我想知道上周我们是怎么在酒吧吃饭的。吉姆想要一份关于它的报告,而你是唯一知道细节的人。
当然可以,但是大概要30分钟吧?
我:太好了,鲍勃。你得到消息后给我回个电话!
这时,我挂了电话。因为我需要鲍勃提供一些信息来完成我的报告,所以我放下报告去喝了杯咖啡,然后又处理了一些电子邮件。40分钟后(Bob反应慢),Bob给我回了电话,告诉了我所需要的信息。在这一点上,我继续我的工作和我的报告,因为我有所有我需要的信息。
想象一下,如果谈话是这样进行的;
我:嗨,鲍勃,我想知道上周我们是怎么在酒吧吃饭的。吉姆想要一份关于它的报告,而你是唯一知道细节的人。
当然可以,但是大概要30分钟吧?
我:太好了,鲍勃。我将等待。
我坐在那里等着。等着。等着。40分钟。除了等待什么都不做。最后,鲍勃给了我信息,我们挂了电话,我完成了我的报告。但我失去了40分钟的工作效率。
这是异步与同步行为
这正是我们问题中所有例子所发生的。加载图像、从磁盘加载文件以及通过AJAX请求页面都是缓慢的操作(在现代计算环境中)。
Rather than waiting for these slow operations to complete, JavaScript lets you register a callback function which will be executed when the slow operation has completed. In the meantime, however, JavaScript will continue to execute other code. The fact that JavaScript executes other code whilst waiting for the slow operation to complete makes the behaviorasynchronous. Had JavaScript waited around for the operation to complete before executing any other code, this would have been synchronous behavior.
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将继续处理下一行代码;即警报(outerScopeVar)。
这就是为什么我们看到警报显示为undefined;因为alert()是立即处理的,而不是在图像加载之后。
为了修复代码,我们所要做的就是将警报(outerScopeVar)代码移动到回调函数中。因此,我们不再需要将outerScopeVar变量声明为全局变量。
var img = document.createElement('img');
img.onload = function() {
var localScopeVar = this.width;
alert(localScopeVar);
};
img.src = 'lolcat.png';
你总是会看到一个回调被指定为一个函数,因为这是JavaScript中定义一些代码,但直到以后才执行它的唯一方法。
因此,在我们所有的例子中,函数(){/* Do something */}是回调;要修复所有的示例,我们所要做的就是将需要操作响应的代码移到那里!
*从技术上讲,你也可以使用eval(),但eval()在这个目的上是邪恶的
如何让来电者久等?
您当前可能有一些类似于此的代码;
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);
然而,我们现在知道返回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)。