Fabrício的答案就在现场。但我想用一些不太技术的东西来补充他的答案,该技术着重于类比,以帮助解释异步性的概念。
比喻...
昨天,我正在做的工作需要同事提供一些信息。我给他打电话。对话过程如下:
我:嗨鲍勃,我需要知道我们如何富 “d的酒吧 ”上周天。吉姆想要一份报告,而您是唯一知道此事细节的人。
鲍勃:好的,但是我要花30分钟左右吗?
我:太好了,鲍勃。了解信息后给我回电话!
此时,我挂了电话。由于我需要鲍勃提供的信息来完成我的报告,因此我离开了报告,去喝咖啡,然后挂了一些电子邮件。40分钟后(鲍勃很慢),鲍勃回电话给了我我需要的信息。至此,我有了我需要的所有信息,就可以继续处理报告了。
想象一下,如果谈话像这样进行了;
我:嗨鲍勃,我需要知道我们如何富 “d的酒吧 ”上周天。吉姆想要一份关于它的报告,而您是唯一知道有关它的细节的报告。
鲍勃:好的,但是我要花30分钟左右吗?
我:太好了,鲍勃。我会等。
我坐在那里等。并等待。并等待。40分钟。除了等待,无所事事。最终,鲍勃给了我信息,我们挂断了电话,我完成了报告。但是我失去了40分钟的生产力。
这是异步还是同步行为
这正是我们问题中所有示例所发生的情况。加载映像,从磁盘加载文件以及通过AJAX请求页面都是缓慢的操作(在现代计算的背景下)。
JavaScript无需等待这些慢速操作完成,而是让您注册一个回调函数,该回调函数将在慢速操作完成后执行。同时,JavaScript将继续执行其他代码。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)
代码移到回调函数中。因此,我们不再需要将outerScopeVar
变量声明为全局变量。
var img = document.createElement('img');
img.onload = function() {
var localScopeVar = this.width;
alert(localScopeVar);
};
img.src = 'lolcat.png';
您将始终看到将回调指定为函数,因为这是JavaScript中定义某些代码的唯一*方式,但要等到以后再执行。
因此,在我们所有的示例中,function() { /* 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);
但是,我们现在知道这种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
)。