Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/469.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 为什么我的变量在函数内部修改后保持不变异步代码引用_Javascript_Asynchronous - Fatal编程技术网

Javascript 为什么我的变量在函数内部修改后保持不变异步代码引用

Javascript 为什么我的变量在函数内部修改后保持不变异步代码引用,javascript,asynchronous,Javascript,Asynchronous,鉴于以下示例,为什么在所有情况下都未定义outerScopeVar var outerScopeVar; var img = document.createElement('img'); img.onload = function() { outerScopeVar = this.width; }; img.src = 'lolcat.png'; alert(outerScopeVar); 为什么在所有这些示例中它都输出未定义的?我不想采取变通办法,我想知道发生这种情况的原

鉴于以下示例,为什么在所有情况下都未定义
outerScopeVar

var outerScopeVar;

var img = document.createElement('img');
img.onload = function() {
    outerScopeVar = this.width;
};
img.src = 'lolcat.png';
alert(outerScopeVar);

为什么在所有这些示例中它都输出
未定义的
?我不想采取变通办法,我想知道发生这种情况的原因


注意:这是JavaScript异步性的一个典型问题。请随意改进此问题,并添加更多社区可以认同的简化示例


一个词的答案是:异步性

前言 在堆栈溢出中,这个主题已经被迭代了至少几千次。因此,首先我想指出一些非常有用的资源:

  • 。请参阅他解释同步和异步流的优秀答案,以及“重构代码”部分。
    @Benjamin Gruenbaum也花了大量精力解释同一线程中的异步性

  • 还以简单的方式很好地解释了异步性


对眼前问题的回答 让我们首先追踪常见的行为。在所有示例中,
outerScopeVar
都是在函数内部修改的。该函数显然不是立即执行的,而是作为参数赋值或传递的。这就是我们所说的回调

现在的问题是,什么时候调用回调

这要视情况而定。让我们再次尝试跟踪一些常见行为:

  • img.onload
    可能在将来某个时候调用,当(并且如果)图像已成功加载时
  • setTimeout
    可能在延迟过期且未被
    cleartimout
    取消超时后的某个时间调用。注意:即使使用
    0
    作为延迟,所有浏览器都有一个最小超时延迟上限(在HTML5规范中指定为4ms)
  • jQuery
    $.post
    的回调可能会在将来某个时候调用,当(如果)Ajax请求成功完成时
  • Node.js的
    fs.readFile
    可能在将来某个时候被调用,当文件被成功读取或抛出错误时
在所有情况下,我们都有一个回调,它可能在将来某个时候运行。这个“将来某个时候”就是我们所说的异步流

异步执行从同步流中推出。也就是说,当同步代码堆栈正在执行时,异步代码将永远不会执行。这就是JavaScript是单线程的含义

更具体地说,当JS引擎空闲时(不执行一堆同步代码),它将轮询可能触发异步回调的事件(例如过期超时、收到的网络响应),并逐个执行。这被认为是错误的

也就是说,手绘红色形状中突出显示的异步代码只能在其各自代码块中的所有剩余同步代码执行后执行:

简而言之,回调函数是同步创建的,但异步执行。在知道异步函数已经执行之前,您不能依赖于它的执行,如何做到这一点

其实很简单。依赖于异步函数执行的逻辑应该从此异步函数内部启动/调用。例如,在回调函数中移动
警报
s和
控制台.log
s也会输出预期结果,因为此时结果可用

实现自己的回调逻辑 通常,您需要对异步函数的结果执行更多操作,或者根据调用异步函数的位置对结果执行不同的操作。让我们来处理一个更复杂的示例:

var outerScopeVar;
helloCatAsync();
alert(outerScopeVar);

function helloCatAsync() {
    setTimeout(function() {
        outerScopeVar = 'Nya';
    }, Math.random() * 2000);
}
注意:我使用带有随机延迟的
setTimeout
作为通用异步函数,相同的示例适用于Ajax、
readFile
onload
和任何其他异步流

此示例显然与其他示例存在相同的问题,它不会等待异步函数执行

让我们通过实现我们自己的回调系统来解决这个问题。首先,我们去掉了丑陋的
outerScopeVar
,它在本例中完全无用。然后我们添加一个接受函数参数的参数,我们的回调函数。异步操作完成后,我们调用此回调传递结果。实施情况(请按顺序阅读评论):

上述示例的代码段:

//1。通过回调函数调用helloCatAsync,
//接收异步操作的结果时将调用
console.log(“1.调用的函数…”)
helloCatAsync(函数(结果){
//5.从异步函数接收结果,
//现在用它做你想做的事:
控制台日志(“5.结果为:”,结果);
});
// 2. “callback”参数是对函数的引用,该函数
//作为helloCatAsync调用的参数传递
函数helloCatAsync(回调){
log(“2.callback这里是作为上述参数传递的函数…”)
//3.启动异步操作:
setTimeout(函数(){
console.log(“3.启动异步操作…”)
log(“4.完成异步操作,调用回调,传递结果…”)
//4.完成异步操作,
//调用将结果作为参数传递的回调函数
回调('Nya');
},Math.random()*2000);
}
Fabricio的回答很贴切;但我想用一些不那么技术性的东西来补充他的回答,重点放在一个类比上,以帮助解释异步性的概念


一个类比。。。 耶斯特
// 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);
var outerScopeVar;
helloCatAsync();
alert(outerScopeVar);

function helloCatAsync() {
    setTimeout(function() {
        outerScopeVar = 'Nya';
    }, Math.random() * 2000);
}
// 1. Call helloCatAsync passing a callback function,
//    which will be called receiving the result from the async operation
helloCatAsync(function(result) {
    // 5. Received the result from the async function,
    //    now do whatever you want with it:
    alert(result);
});

// 2. The "callback" parameter is a reference to the function which
//    was passed as argument from the helloCatAsync call
function helloCatAsync(callback) {
    // 3. Start async operation:
    setTimeout(function() {
        // 4. Finished async operation,
        //    call the callback passing the result as argument
        callback('Nya');
    }, Math.random() * 2000);
}
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);
var img = document.createElement('img');

img.onload = function() {
    var localScopeVar = this.width;
    alert(localScopeVar);
};

img.src = 'lolcat.png';
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);
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);
});
function getMessage() {
  var outerScopeVar;
  setTimeout(function() {
    outerScopeVar = 'Hello asynchronous world!';
  }, 0);
  return outerScopeVar;
}
console.log(getMessage());
function getMessage(callback) {
  setTimeout(function() {
    callback('Hello asynchronous world!');
  }, 0);
}
getMessage(function(message) {
  console.log(message);
});
function getMessage() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Hello asynchronous world!');
    }, 0);
  });
}

getMessage().then(function(message) {
  console.log(message);  
});
function getMessage() {
  var deferred = $.Deferred();
  setTimeout(function() {
    deferred.resolve('Hello asynchronous world!');
  }, 0);
  return deferred.promise();
}

getMessage().done(function(message) {
  console.log(message);  
});
function getMessage () {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve('Hello asynchronous world!');
        }, 0);
    });
}

async function main() {
    let message = await getMessage();
    console.log(message);
}

main();
var outerScopeVar;  //line 1
$.post('loldog', function(response) {  //line 2
    outerScopeVar = response;
});
alert(outerScopeVar);  //line 3
var outerScopeVar; //line 1
$.post('loldog', function(response) {  //line 2, takes 10 seconds to complete
    outerScopeVar = response;
});
alert("Lets wait for some time here! Waiting is fun");  //line 3
alert(outerScopeVar);  //line 4
var outerScopeVar;
$.post('loldog', function(response) {
    outerScopeVar = response;
    alert(outerScopeVar);
});