为什么我的变量不变后,我修改一个函数里面? - 异步code参考 [英] Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference

查看:165
本文介绍了为什么我的变量不变后,我修改一个函数里面? - 异步code参考的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑下面的例子

,为什么是未定义 outerScopeVar 在所有情况下?

  VAR outerScopeVar;VAR IMG =使用document.createElement('IMG');
img.onload =功能(){
    outerScopeVar = this.width;
};
img.src ='lolcat.png';
警报(outerScopeVar);

  VAR outerScopeVar;
的setTimeout(函数(){
    outerScopeVar ='你好异步世界!;
},0);
警报(outerScopeVar);

使用一些jQuery

  //例
VAR outerScopeVar;
.post的$('loldog',函数(响应){
    outerScopeVar =响应;
});
警报(outerScopeVar);

  // Node.js的例子
VAR outerScopeVar;
fs.readFile('./ catdog.html',函数(ERR,数据){
    outerScopeVar =数据;
});
的console.log(outerScopeVar);

  //与承诺
VAR outerScopeVar;
myPromise.then(功能(响应){
    outerScopeVar =响应;
});
的console.log(outerScopeVar);

为什么它的输出未定义在所有这些例子吗?我不想解决方法,我想知道的为什么这种情况正在发生。



  

注意:这是对的 JavaScript的异步性的规范问题的。随意改进这个问题,并增加更多的简化的例子可以使公众能够认同。



解决方案

一个词来回答:异步性

前言

这个主题已经迭代至少十万次一对夫妇,在这里,在堆栈溢出。因此,首先,我想指出一些非常有用的资源:


手边的问题的答案

让我们先来追溯共同的行为。在所有的例子中, outerScopeVar 功能的内部修改。显然不是立即执行的功能,它被分配或作为参数传递。这就是我们所说的 回调

现在的问题是,当该回调叫什么名字?

这取决于情况。让我们再次尝试跟踪一些常见的行为:


  • img.onload 可称为的在将来某个时候的时候(如果)图像已加载成功。

  • 的setTimeout 可称为的在将来某个时候的,之后的延迟已过期,超时没有被取消 clearTimeout 。注意:使用甚至当 0 延迟,所有的浏览器有一个最低超时延迟帽(指定要在HTML5规范的4ms)

  • 的jQuery $。POST 的回调可以被称为的在将来某个时候的时候(如果)Ajax请求已成功完成

  • 的Node.js的 fs.readFile 可称为的在将来某个时候的,当该文件已被成功读取或抛出一个错误。

在任何情况下,我们有可能会遇到的回调在将来某个时候的。这种在未来某个时候,也就是我们所说的异步流

异步执行被推出同步流。也就是说,异步code将绝不会而同步code堆栈正在执行执行。这是JavaScript的是单线程的含义。

更具体地,当JS引擎是空闲 - 不执行(一个)的同步code的叠层 - 其将轮询可能触发异步回调事件(例如过期超时,接收的网络响应)和执行它们一个接一个。这被视为事件循环

这就是异步code在手绘红色的形状可以突出显示后,才在各自的code阻断所有剩余的同步code已执行执行:

在短,回调函数被同步地创建,但异步执行。你不能依赖于异步函数的执行,直到你知道它已经执行,该怎么做?

这很简单,真的。依赖于异步功能执行的逻辑应启动/从该异步函数内部调用。例如,移动警报的console.log s到回调函数将输出预期的结果里面,因为其结果是可以在这一点上。

实现自己的回调逻辑

您经常需要从异步函数,结果做更多​​的事情,或者从那里异步函数被调用根据结果做不同的事情。让我们来解决复杂一点的例子:

  VAR outerScopeVar;
helloCatAsync();
警报(outerScopeVar);功能helloCatAsync(){
    的setTimeout(函数(){
        outerScopeVar ='妗';
    },的Math.random()* 2000);
}

注意:我用的setTimeout 与随机延迟作为通用异步函数,相同的例子适用于阿贾克斯, READFILE 的onload 和任何其他异步流。

此实施例清楚地从相同的问题遭受的其他实例中,它不等到异步函数执行

让我们来解决它实现我们自己的回调制度。首先,我们摆脱难看的 outerScopeVar 这是在这种情况下,完全无用的。然后,我们添加它接受一个函数参数,回调的参数。当异步操作完成,我们称之为回调传递的结果。实现(请按顺序阅读评论)

  // 1.呼叫helloCatAsync传递一个回调函数,
//这将被称为从异步操作接收结果
helloCatAsync(功能(结果){
    // 5.收到异步函数的结果,
    //现在做任何你想用它:
    警报(结果);
});// 2.回调参数是对​​函数的引用它
//从helloCatAsync调用参数传递
功能helloCatAsync(回调){
    // 3.启动异步操作:
    的setTimeout(函数(){
        // 4完成异步操作,
        //调用回调传递结果作为论据
        回调('妗');
    },的Math.random()* 2000);
}

大多数情况下在实际使用的情况下,DOM API和大多数图书馆已经提供的回调功能(在这个例子示范的 helloCatAsync 实现)。你只需要通过回调函数和理解,它将执行出流同步,并调整你的code,以适应这一点。

您还会注意到,由于异步特性,这是不可能收益从异步流回哪里被定义回调流同步,作为一种价值异步回调同步code已经执行完毕后执行长

而不是收益从异步回调ING一个值,你将不得不利用回调格局,还是......承诺。

的承诺

虽然有很多方法可以保持在香草JS海湾回调地狱,承诺越来越受欢迎,目前在ES6被标准化(看<一个href=\"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise\">Promise - MDN )

承诺(又名期货)提供更线性,从而宜人,读书异步code,但解释他们的整个功能是出了这个问题的范围。相反,我会离开这些优秀的资源,为有兴趣的:


关于JavaScript的异步性

更多阅读材料



  

注意:我标志着这个答案社区维基,因此,至少有100人的声誉可以编辑和完善它!请随意改进这个答案,或者提交一个完全新的答案,如果你想为好。


  
  

我希望把这个问题变成一个规范的话题回答这是无关的Ajax的异步性的问题(有如何返回响应从AJAX调用?为),因此这个话题需要你的帮助一样好,乐于助人越好!


Given the following examples, why is outerScopeVar undefined in all cases?

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);

Why does it output undefined in all of these examples? I don't want workarounds, I want to know why this is happening.


Note: This is a canonical question for JavaScript asynchronicity. Feel free to improve this question and add more simplified examples which the community can identify with.

解决方案

One word answer: asynchronicity.

Forewords

This topic has been iterated at least a couple of thousands times, here, in Stack Overflow. Hence, first off I'd like to point out some extremely useful resources:


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. 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.
  • setTimeout may be called sometime in the future, after the delay has expired and the timeout hasn't been cancelled by clearTimeout. Note: even when using 0 as delay, all browsers have a minimum timeout delay cap (specified to be 4ms in the HTML5 spec).
  • jQuery $.post's callback may be called sometime in the future, when (and if) the Ajax request has been completed successfully.
  • Node.js's fs.readFile may be called sometime in the future, when the file has been read successfully or thrown an error.

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, asynchronous code will never execute while the synchronous code stack is executing. This is the meaning of JavaScript being single-threaded.

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 (e.g. expired timeout, received network response) and execute them one after another. This is regarded as Event Loop.

That is, the asynchronous code highlighted in the hand-drawn red shapes may execute only after all the remaining synchronous code in their respective code blocks have executed:

In short, the callback functions are created synchronously, but executed asynchronously. You just can't rely on the execution of an asynchronous function until you know it has executed, and how to do that?

It is simple, really. The logic that depends on the asynchronous function execution should be started/called from inside this asynchronous function. For example, moving the alerts and console.logs to inside the callback function would output the expected result, because the result is available at that point.

Implementing your own callback logic

Often you need to do more things with the result from an asynchronous function, or do different things with the result depending from where the asynchronous function has been called. Let's tackle a bit more complex example:

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

function helloCatAsync() {
    setTimeout(function() {
        outerScopeVar = 'Nya';
    }, Math.random() * 2000);
}

Note: I'm using setTimeout with a random delay as a generic asynchronous function, the same example applies to Ajax, readFile, onload and any other asynchronous flow.

This example clearly suffers from the same issue as the other examples, it is not waiting until the asynchronous function executes.

Let's tackle it implementing a callback system of our own. First off, we get rid of that ugly outerScopeVar which is completely useless in this case. Then we add a parameter which accepts a function argument, our callback. When the asynchronous operation finishes, we call this callback passing the result. The implementation (please read the comments in order):

// 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);
}

Most often in real use cases, the DOM API and most libraries already provide the callback functionality (the helloCatAsync implementation in this demonstrative example). You only need to pass the callback function and understand that it will execute out of the synchronous flow, and restructure your code to accommodate for that.

You will also notice that due to the asynchronous nature, it is impossible to return a value from an asynchronous flow back to the synchronous flow where the callback was defined, as the asynchronous callbacks are executed long after the synchronous code has already finished executing.

Instead of returning a value from an asynchronous callback, you will have to make use of the callback pattern, or... Promises.

Promises

Although there are ways to keep the callback hell at bay with vanilla JS, promises are growing in popularity and are currently being standardized in ES6 (see Promise - MDN).

Promises (a.k.a. Futures) provide a more linear, and thus pleasant, reading of asynchronous code, but explaining their entire functionality is out of the scope of this question. Instead, I'll leave these excellent resources for the interested:


More reading material about JavaScript asynchronicity


Note: I've marked this answer as Community Wiki, hence anyone with at least 100 reputation can edit and improve it! Please feel free to improve this answer, or submit a completely new answer if you'd like as well.

I want to turn this question into a canonical topic to answer asynchronicity issues which are unrelated to Ajax (there is How to return the response from an AJAX call? for that), hence this topic needs your help to be as good and helpful as possible!

这篇关于为什么我的变量不变后,我修改一个函数里面? - 异步code参考的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆