本文翻译自:Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
Given the following examples, why is outerScopeVar
undefined in all cases? 给定以下示例,为什么在所有情况下都未定义outerScopeVar
?
var outerScopeVar;
var img = document.createElement('img');
img.onload = function() {
outerScopeVar = this.width;
};
img.src = 'lolcat.png';
alert(outerScopeVar);
var outerScopeVar;
setTimeout(function() {
outerScopeVar = 'Hello Asynchronous World!';
}, 0);
alert(outerScopeVar);
// Example using some jQuery
var outerScopeVar;
$.post('loldog', function(response) {
outerScopeVar = response;
});
alert(outerScopeVar);
// Node.js example
var outerScopeVar;
fs.readFile('./catdog.html', function(err, data) {
outerScopeVar = data;
});
console.log(outerScopeVar);
// with promises
var outerScopeVar;
myPromise.then(function (response) {
outerScopeVar = response;
});
console.log(outerScopeVar);
// geolocation API
var outerScopeVar;
navigator.geolocation.getCurrentPosition(function (pos) {
outerScopeVar = pos;
});
console.log(outerScopeVar);
Why does it output undefined
in all of these examples? 为什么在所有这些示例中都输出undefined
? I don't want workarounds, I want to know why this is happening. 我不想要解决方法,我想知道为什么会这样。
Note: This is a canonical question for JavaScript asynchronicity . 注意:这是JavaScript异步性的典型问题。 Feel free to improve this question and add more simplified examples which the community can identify with. 随时改进此问题,并添加更多简化的示例,社区可以识别。
#1楼
参考:https://stackoom.com/question/1bItC/在函数内部修改变量后-为什么变量未更改-异步代码参考
#2楼
One word answer: asynchronicity . 一句话回答: 异步性 。
Forewords 前言
This topic has been iterated at least a couple of thousands of times, here, in Stack Overflow. 在Stack Overflow中,该主题已至少迭代了数千次。 Hence, first off I'd like to point out some extremely useful resources: 因此,首先,我想指出一些非常有用的资源:
@Felix Kling's answer to "How do I return the response from an asynchronous call?" @Felix Kling对“如何从异步调用返回响应?”的回答 . 。 See his excellent answer explaining synchronous and asynchronous flows, as well as the "Restructure code" section. 请参阅他出色的解释同步和异步流的答案,以及“重组代码”部分。
@Benjamin Gruenbaum has also put a lot of effort explaining asynchronicity in the same thread. @Benjamin Gruenbaum还付出了很多努力来解释同一线程中的异步性。@Matt Esch's answer to "Get data from fs.readFile" also explains asynchronicity extremely well in a simple manner. @Matt Esch对“从fs.readFile获取数据”的回答也很好地解释了异步性。
The answer to the question at hand 眼前问题的答案
Let's trace the common behavior first. 让我们首先跟踪常见行为。 In all examples, the outerScopeVar
is modified inside of a function . 在所有示例中, outerScopeVar
在function内部进行修改。 That function is clearly not executed immediately, it is being assigned or passed as an argument. 该函数显然不会立即执行,而是被分配或作为参数传递。 That is what we call a callback . 这就是我们所说的回调 。
Now the question is, when is that callback called? 现在的问题是,何时调用该回调?
It depends on the case. 这要视情况而定。 Let's try to trace some common behavior again: 让我们尝试再次跟踪一些常见行为:
-
img.onload
may be called sometime in the future , when (and if) the image has successfully loaded.img.onload
可能在将来的某个时间(如果(如果))图像成功加载img.onload
调用。 -
setTimeout
may be called sometime in the future , after the delay has expired and the timeout hasn't been canceled byclearTimeout
.setTimeout
可能会在延迟到期后并且clearTimeout
尚未取消超时之后的将来某个时间调用。 Note: even when using0
as delay, all browsers have a minimum timeout delay cap (specified to be 4ms in the HTML5 spec). 注意:即使将0
用作延迟,所有浏览器都具有最小超时延迟上限(在HTML5规范中指定为4ms)。 - jQuery
$.post
's callback may be called sometime in the future , when (and if) the Ajax request has been completed successfully. jQuery$.post
的回调可能在将来的某个时间(当Ajax请求已成功完成时)被调用。 - Node.js's
fs.readFile
may be called sometime in the future , when the file has been read successfully or thrown an error. 当文件已被成功读取或引发错误时, 将来可能会调用Node.js的fs.readFile
。
In all cases, we have a callback which may run sometime in the future . 在所有情况下,我们都有一个回调,它可能在将来的某个时间运行。 This "sometime in the future" is what we refer to as asynchronous flow . 这种“将来的某个时候”就是我们所说的异步流 。
Asynchronous execution is pushed out of the synchronous flow. 异步执行从同步流中推出。 That is, the asynchronous code will never execute while the synchronous code stack is executing. 也就是说,异步代码将永远不会在同步代码堆栈执行时执行。 This is the meaning of JavaScript being single-threaded. 这就是JavaScript是单线程的意思。
More specifically, when the JS engine is idle -- not executing a stack of (a)synchronous code -- it will poll for events that may have triggered asynchronous callbacks (eg expired timeout, received network response) and execute them one after another. 更具体地说,当JS引擎处于空闲状态时-不执行(a)同步代码的堆栈-它将轮询可能触发异步回调的事件(例如