함수 내에서 수정한 후에도 변수가 변경되지 않은 상태로 유지됩니까? - 비동기 코드 참조
P粉403804844
2023-08-28 11:46:28
<p>鉴于以下示例,为什么 <code>outerScopeVar</code> 거기에 어떤 정보가 있나요?</p>
<pre class="brush:php;toolbar:false;">var externalScopeVar;
var img = document.createElement('img');
img.onload = 함수() {
externalScopeVar = this.width;
};
img.src = 'lolcat.png';
Alert(outerScopeVar);</pre>
<pre class="brush:php;toolbar:false;">var externalScopeVar;
setTimeout(함수() {
externalScopeVar = '안녕하세요 비동기 세계!';
}, 0);
Alert(outerScopeVar);</pre>
<pre class="brush:php;toolbar:false;">// 일부 jQuery를 사용한 예
var externalScopeVar;
$.post('loldog', function(response) {
externalScopeVar = 응답;
});
Alert(outerScopeVar);</pre>
<pre class="brush:php;toolbar:false;">// Node.js 예
var externalScopeVar;
fs.readFile('./catdog.html', function(err, data) {
externalScopeVar = 데이터;
});
console.log(outerScopeVar);</pre>
<pre class="brush:php;toolbar:false;">// 약속 있음
var externalScopeVar;
myPromise.then(함수(응답)
externalScopeVar = 응답;
});
console.log(outerScopeVar);</pre>
<pre class="brush:php;toolbar:false;">// 관찰 가능 항목 포함
var externalScopeVar;
myObservable.subscribe(함수 (값) {
externalScopeVar = 값;
});
console.log(outerScopeVar);</pre>
<pre class="brush:php;toolbar:false;">// 위치정보 API
var externalScopeVar;
navigator.geolocation.getCurrentPosition(함수 (pos) {
외부 범위Var = pos;
});
console.log(outerScopeVar);</pre>
<p>为什么에서 <code>정의되지 않음</code> ?我不需要解决方法,我想知道<strong>为什么</strong>发生这种情况。</p>
<시간 />
<인용문>
<p><strong>注意:</strong>这是一个关于 JavaScript는 异步性规范问题입니다.示例。</p>
</blockquote><p><br /></p>
Fabrício의 답변은 매우 정확합니다. 하지만 저는 비유를 통해 비동기성의 개념을 설명하는 데 초점을 맞춰 그의 답변을 좀 덜 기술적인 것으로 보완하고 싶습니다.
비유...
어제 제가 하고 있는 일을 위해 동료들로부터 정보를 얻어야 했어요. 나는 그에게 전화를 걸었습니다. 대화는 다음과 같습니다:
이 시점에서 나는 전화를 끊었다. 보고서를 완성하려면 Bob의 정보가 필요했기 때문에 보고서를 놓고 커피 한 잔을 마시고 이메일을 몇 개 읽었습니다. 40분 후(Bob의 속도가 느림) Bob이 다시 전화를 걸어 필요한 정보를 제공했습니다. 이 시점에서는 필요한 정보를 모두 얻었으므로 보고서 작성 작업을 계속합니다.
대화가 이렇게 흘러간다고 상상해보세요
저는 거기 앉아서 기다렸어요. 그리고 기다렸다. 그리고 기다렸다. 40분. 아무것도 하지 말고 기다리세요. 결국 Bob은 나에게 정보를 주었고 우리는 전화를 끊었고 나는 보고서를 마쳤습니다. 그러나 나는 40분의 생산성을 잃었습니다.
이것은 비동기식 대 동기식 동작입니다
이것이 바로 우리 질문의 모든 예에서 일어나는 일입니다. 이미지 로드, 디스크에서 파일 로드, AJAX를 통한 페이지 요청은 모두 현대 컴퓨팅 환경에서 느린 작업입니다.
JavaScript를 사용하면 느린 작업이 완료될 때까지 기다리는 대신 느린 작업이 완료될 때 실행될 콜백 함수를 등록할 수 있습니다. 그러나 그동안 JavaScript는 다른 코드를 계속 실행합니다. JavaScript가 느린 작업이 완료되기를 기다리는 동안 다른 코드를 실행한다는 사실은 동작을 비동기로 만듭니다. JavaScript가 다른 코드를 실행하기 전에 작업이 완료될 때까지 기다리는 경우 이는 동기 동작이 됩니다.
으아악위 코드에서는 JavaScript에
작업입니다. 이 느린 작업이 완료되면 콜백 함수가 실행되지만 그 동안 JavaScript는 다음 코드 줄인lolcat.png
를 로드하도록 요청하는데, 이는lolcat.png
,这是一个sloooow 操作。一旦这个缓慢的操作完成,回调函数就会被执行,但与此同时,JavaScript 将继续处理下一行代码;即alert(outerScopeVar)
sloooowalert(outerScopeVar)
를 계속 처리합니다.未定义
;因为alert()
그래서 이미지가 로드된 후가 아니라alert(outerScopeVar)
代码移到回调函数中。因此,我们不再需要将outerScopeVar
코드를 수정하려면으아악 함수로 지정된 콜백은 항상
보게 됩니다. 이는 JavaScript에서 일부 코드를 정의한 다음 나중에 실행하는 유일한* 방법이기 때문입니다.function() { /* Do Something */ }
따라서 모든 예제에서 는 콜백입니다. 모든* 기술적으로는
eval()
,但是eval()
악을 이 목적으로 사용할 수도 있습니다발신자를 대기 상태로 만드는 방법은 무엇입니까?
현재 이와 유사한 코드가 있을 수 있습니다.
으아악하지만 이제 우리는 알고 있습니다
返回outerScopeVar
会立即发生;在onload
回调函数更新变量之前。这会导致getWidthOfImage()
返回undefined
,并发出警报undefined
.이 문제를 해결하려면
...전과 마찬가지로 전역 변수를 제거할 수 있었습니다(이 경우getWidthOfImage()
호출하는 함수가 콜백을 등록한 다음 해당 콜백 내로 너비 경고를 이동하도록 허용해야 합니다. 으아악).
width
한 단어로 대답: 비동시성.
머리말
이 주제는 Stack Overflow에서 최소 수천 번 반복되었습니다. 먼저 매우 유용한 리소스를 알려드리고 싶습니다.
"비동기 호출에서 응답을 반환하는 방법은 무엇입니까?"에 대한 @Felix Kling의 답변입니다. 동기 및 비동기 프로세스를 설명하는 그의 훌륭한 답변과 "코드 재구성" 섹션을 참조하세요.
@Benjamin Gruenbaum도 동일한 스레드 내에서 비동기성을 설명하기 위해 많은 노력을 기울였습니다.
"fs.readFile에서 데이터 가져오기"에 대한 @Matt Esch의 답변 또한 간단한 방법으로 비동기성을 아주 잘 설명합니다.
현재 질문에 대한 답변
먼저 일반적인 행동을 추적해 보겠습니다. 모든 예에서
outerScopeVar
는 함수 내에서 수정됩니다. 이 함수는 분명히 즉시 실행되지 않으며 매개변수로 할당되거나 전달됩니다. 이것을 콜백이라고 부릅니다.이제 질문은 이 콜백이 언제 호출되는가입니다.
상황에 따라 다릅니다. 몇 가지 일반적인 행동을 다시 추적해 보겠습니다.
img.onload
언제든지 이라고 할 수 있습니다. (만약) 이미지가 성공적으로 로드되었습니다.setTimeout
은 지연이 만료되고clearTimeout
에 의해 시간 초과가 취소되지 않은 후setTimeout
可能会在延迟到期且超时尚未被clearTimeout
取消后在将来的某个时间被调用。注意:即使使用0
향후 언젠가0
을 지연으로 사용하는 경우에도 모든 브라우저에는 최소 시간 초과 지연(HTML5 사양에서 4밀리초로 지정됨)에 대한 상한이 있습니다.$.post
jQueryfs.readFile
Node.js' 는 파일을 성공적으로 읽었거나 오류가 발생하면 미래에모든 경우에 미래에 언젠가 실행될 수 있는 콜백이 있습니다. 이 "미래의 언젠가"를 우리는 비동기 흐름
이라고 부릅니다.비동기 실행은 동기 프로세스에서 푸시됩니다. 즉, 동기 코드 스택이 실행되는 동안 비동기 코드는 영원히
실행됩니다. 이것이 JavaScript의 단일 스레드입니다.좀 더 구체적으로 말하자면, JS 엔진이 여러 동기 코드를 실행하지 않고 유휴 상태일 때 비동기 콜백(예: 시간 초과, 수신된 네트워크 응답)을 트리거할 수 있는 이벤트를 폴링하고 차례로 실행합니다. 이는 이벤트 루프
로 간주됩니다.정말 간단해요. 비동기 함수 실행에 의존하는 로직은 해당 비동기 함수 내에서 시작/호출되어야 합니다. 예를 들어 콜백 함수 내에서
alert
和console.log
를 이동하면 해당 시점에 결과를 사용할 수 있으므로 예상한 결과가 출력됩니다.자신만의 콜백 로직을 구현하세요
비동기 함수의 결과에 대해 더 많은 작업을 수행해야 하거나 비동기 함수가 호출되는 위치에 따라 결과에 대해 다른 작업을 수행해야 하는 경우가 많습니다. 좀 더 복잡한 예를 살펴보겠습니다.
으아악참고: 저는 일반 비동기 함수로 임의 지연과 함께
setTimeout
를 사용하고 있습니다. 동일한 예가 Ajax, readFile, onload 및 기타 비동기 흐름에 적용됩니다.이 예제에는 분명히 다른 예제와 동일한 문제가 있습니다. 비동기 함수가 실행될 때까지 기다리지 않습니다.
자체 콜백 시스템을 구현하여 이 문제를 해결해 보겠습니다. 먼저, 이 경우에는 전혀 쓸모가 없는 추악한
으아악outerScopeVar
을 제거합니다. 그런 다음 함수 인수인 콜백을 허용하는 매개변수를 추가합니다. 비동기 작업이 완료되면 이 콜백을 호출하고 결과를 전달합니다. 구현 (댓글을 순서대로 읽어주세요):위 예의 코드 조각: