回答

收藏

为什么我的变量在函数内部修改后没有改变?- 异步代码参考

技术问答 技术问答 256 人阅读 | 0 人回复 | 2023-09-12

为什么要考虑以下示例outerScopeVar一切都是undefined?- c( e/ o  f& t' C4 @6 o
    var outerScopeVar;var img = document.createElement('img');img.onload = function()      outerScopeVar = this.width;};img.src = 'lolcat.png';alert(outerScopeVar);% B' u1 [; p! u) t3 X% H# ]0 M+ y# b
    var outerScopeVar;setTimeout(function()      outerScopeVar = 'Hello Asynchronous World!;alert(outerScopeVar);
    ( a9 x3 U8 M1 w/ M) d
    // Example using some jQueryvar outerScopeVar;$.post('loldog',function(response)    outerScopeVar = response;});alert(outerScopeVar);$ T' @0 D/ R$ O2 [) o" o
    // Node.js examplevar outerScopeVar;fs.readFile('./catdog.html',function(err,data)    outerScopeVar = data;});console.log(outerScopeVar);
    - _" D! ?- q" B( W
    // with promisesvar outerScopeVar;myPromise.then(function (response)    outerScopeVar = response;});console.log(outerScopeVar);
    0 P1 o1 b6 \6 O! b' k+ s" e
    // geolocation APIvar outerScopeVar;navigator.geolocation.getCurrentPosition(function (pos)    outerScopeVar = pos;});console.log(outerScopeVar);! b- s4 q# k, a- P
为什么它会undefined输出所有这些例子?我不想要解决方案,我想知道为什么会这样。
9 v- N8 u) X0 ^7 I; X注意:这是JavaScript 异步性标准问题。随意改进这一问题,并添加更简化的社区识别示例。
& \6 u( h0 D; z( K
                                                                3 P8 b1 W  s. t" s' @+ w5 l
    解决方案:                                                                8 k* r+ ]# I% z$ E/ J7 J2 v0 }
                                                                让我们先跟踪常见的行为。在所有的例子中,outerScopeVar都在函数内部修改。显然,该函数不会立即执行,而是被分配或作为参数传输。这就是我们所说的*回调*4 P; _  b. f! |6 n5 ]5 G
现在的问题是什么时候调用回调?
8 |* Q( O) r' A4 Q这取决于情况。让我们再次尝试跟踪一些常见的行为:7 T8 c# w9 B7 s) I+ o4 h6 [" k
img.onload可能会在未来当图像成功加载时,调用时间。9 ~6 H7 L9 f* T' c: \+ |3 l
setTimeout可能会在将来的某个时间调用,延迟到期,超时未取消 clearTimeout。注:即使使用0作为延迟,所有浏览器也有最小的超时延迟上限(在 HTML5 规范指定 44ms)。
0 Z3 J% @; Y3 B6 X8 Q) z$ ljQuery$.post的回调可能将来会有一个当(和如果)调用时间Ajax 请求成功完成时。
7 x: G0 p- p" v0 h* K  O+ ?/ P  G' o& c当文件成功读取或抛出错误时,Node.jsfs.readFile可能在未来的某个时候被调用。
在所有情况下,我们都有可能在未来的某个时候操作回调。这个未来的某个时候就是我们所说的异步流8 `5 D- M2 V$ ?! V3 w5 S) E
同步流程推出异步执行。换句话说,异步代码永远不会执行同步代码堆栈时执行。JavaScript 是单线程的含义。2 O* u7 d& [2 u0 s2 u  w5 k
更具体地说,当 JS 发动机空闲时间-不执行一堆(a)同步代码-它一个接一个地执行轮询可能触发异步回调的事件(如加班和接收网络响应)。这被视为事件循环。4 |9 R) p3 f4 p+ Y
也就是说,手绘红色形状中突出的异步代码只能在其各自代码块中的所有剩余同步代码完成后执行:6 [: G- h3 _! \( B

$ M9 I/ I9 v$ G, }. n' m简而言之,回调函数是同步创建但异步执行的。在你知道异步函数已经执行之前,你不能依赖它的执行,以及如何做到这一点?
8 T+ c% r: f( [7 z这很简单,真的。依赖异步函数执行的逻辑应该从这个异步函数开始/调用。例如,在回调函数中移动alerts 和console.logs 输出预期结果,因为结果可用于此点。
0 y4 N/ W' r# B* W- Y实现自己的回调逻辑通常,您需要更多地操作异步函数的结果,或根据调用异步函数的位置执行不同的结果。让我们处理一个更复杂的例子:# x9 t' C, b) V" }) o: F, ~
[code]var outerScopeVar;helloCatAsync();alert(outerScopeVar);function helloCatAsync()      setTimeout(function()          outerScopeVar = 'NyaMath.random() * 2000)code]注意:我使用setTimeout同样的例子适用于 Ajax readFile、onload还有其他异步流。
( \( |3 \8 o# I. d这个例子显然和其他例子有同样的问题,它不会等到异步函数执行。7 n) h; }1 ~' v) L# y5 P
让我们解决它,实现我们自己的回调系统。首先,我们摆脱了outerScopeVar在这种情况下,完全无用的丑陋。然后我们添加一个接受函数参数的参数,我们的回调。当异步操作完成时,我们调用此回调传输结果。实现(请按顺序阅读评论):4 v/ p- A; m/ h$ z% x; x2 m4 N
[code]// 1. Call helloCatAsync passing a callback function,//   which will be called receiving the result from the async operationhelloCatAsync(function(result)     . 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 callfunction helloCatAsync(callback)      . Start async operation:    setTimeout(function() {        // 4. Finished async operation,                            call the callback passing the result as argument        callback('NyaMath.random() * 2000)code]上述例子的代码片段:
& M( v. {% s$ A+ w6 W  _[code]// 1. Call helloCatAsync passing a callback function,//   which will be called receiving the result from the async operationconsole.log("1. function called...")helloCatAsync(function(result)     . Received the result from the async function,   //   now do whatever you want with it:    console.log("5. result is: ",result);});// 2. The "callback" parameter is a reference to the function which//   was passed as argument from the helloCatAsync callfunction helloCatAsync(callback)    console.log("2. callback here is the function passed as argument above...")    // 3. Start async operation:    setTimeout(function()    console.log("3. start async operation...")    console.log("4. finished async operation,calling the callback,passing the result...")    . Finished async operation,     call the callback passing the result as argument        callback('NyaMath.random() * 2000)code]在大多数情况下,在实际用例中,DOM API 和大多数数都提供了回调功能(helloCatAsync本示例中的实现)。您只需要传输回调函数并理解它将在同步流之外执行,并重构代码以适应它。% s4 k. ?6 E% I# ~$ g/ x5 v1 m
你还会注意到,由于异步的性质,不可能return异步流中的值返回到定义回调的同步流,因为异步回调只有在同步代码完成并执行后才执行。
: R7 f7 T: p7 h# U不是return要从异步回调中获得值,您必须使用回调模式或…承诺。可能好和有帮助!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则