Fabrício的回答是现场的;但是我想用一些不太技术性的东西来补充他的答案,这些东西专注于类比以帮助解释异步性的概念 。
比喻......
昨天,我所做的工作需要一位同事的一些信息。我打电话给他;这是对话的方式:
我 :嗨鲍勃,我需要知道我们是怎么做的 FOO 是的 酒吧 上周。吉姆想要一份关于它的报告,而你是唯一了解它的细节的人。
短发 :当然可以,但是我需要30分钟左右?
我 :那是伟大的鲍勃。当你掌握了这些信息后,给我一个回铃!
此时,我挂了电话。由于我需要鲍勃提供的信息来完成我的报告,我离开了报告并转而喝咖啡,然后我发现了一些电子邮件。 40分钟后(鲍勃很慢),鲍勃打电话给我,告诉我我需要的信息。此时,我继续使用我的报告工作,因为我拥有了我需要的所有信息。
想象一下,如果谈话已经变成这样了;
我 :嗨鲍勃,我需要知道我们是怎么做的 FOO 是的 酒吧 上周。吉姆想要一份关于它的报告,而你是唯一了解它的细节的人。
短发 :当然可以,但是我需要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
,这是一个 sloooow 操作。这个慢速操作完成后将执行回调函数,但与此同时,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
)。