이 두 가지 최상위 답변 모두 잘못되었습니다. 동시성 모델과 이벤트 루프에 대한 MDN 설명을 확인하고 무슨 일이 일어나고 있는지 명확하게 보여야합니다 (MDN 리소스는 실제 gem 임). 그리고 간단히 setTimeout
하면 코드에 예기치 않은 문제가 추가 될뿐 아니라이 작은 문제를 "해결"할 수 있습니다.
무엇 실제로 여기에가는 것이 아니다 "브라우저가 아직 준비되지 않을 수도 있습니다 아직 동시성 때문"에 따라 또는 뭔가 "각 라인은 큐의 뒤쪽에 추가됩니다 이벤트입니다".
jsfiddle DVK에 의해 제공되는 참으로 문제를 보여,하지만 그의 설명은 올바르지 않습니다.
코드에서 일어나고있는 일은 먼저 이벤트 처리기를 버튼 의 click
이벤트에 연결한다는 것 #do
입니다.
그런 다음 실제로 버튼을 클릭 message
하면 이벤트 핸들러 함수를 참조하여가 생성되어에 추가됩니다 message queue
. (가) 때 event loop
이 메시지가 도달, 그것은을 생성 frame
jsfiddle의 클릭 이벤트 핸들러에 함수 호출 스택에.
그리고 이것이 흥미로운 곳입니다. 우리는 자바 스크립트를 비동기식으로 생각하는 데 익숙해 져서이 작은 사실을 간과하기 쉽습니다 . 다음 프레임을 실행하기 전에 모든 프레임을 완전히 실행해야 합니다. 동시성은 없습니다.
이것은 무엇을 의미 하는가? 메시지 큐에서 함수가 호출 될 때마다 생성 된 스택이 비워 질 때까지 큐를 차단합니다. 또는보다 일반적인 용어로, 함수가 반환 될 때까지 차단됩니다. 그리고 그것은 모든 것을 차단합니다 DOM 렌더링 작업, 스크롤 및 기타를 포함하여 합니다. 확인을 원하면 바이올린에서 장기 실행 작업의 지속 시간을 늘리십시오 (예 : 외부 루프를 10 회 더 실행). 실행 중에는 페이지를 스크롤 할 수 없습니다. 오래 실행되면 브라우저가 페이지를 응답하지 않기 때문에 프로세스 종료 여부를 묻습니다. 프레임이 실행되고 있으며 이벤트 루프와 메시지 큐가 완료 될 때까지 고정됩니다.
그렇다면 왜 텍스트의 부작용이 업데이트되지 않습니까? 당신이 동안 때문에 한 DOM에있는 요소의 값을 변경 - 당신이 할 수있는 console.log()
그 가치 즉시 변경 한 후하고 있음을 볼 수 있다 (이 쇼 DVK의 설명이 정확하지 않은 이유를) 변경 - 브라우저가 고갈에 스택을 기다리고 있습니다 ( on
핸들러 함수 반환) 따라서 메시지가 끝나기 때문에 런타임에 돌연변이 작업에 대한 반응으로 추가 된 메시지를 실행하고 UI에 해당 돌연변이를 반영하기 위해 결국 실행할 수 있습니다. .
실제로 코드 실행이 완료되기를 기다리고 있기 때문입니다. 우리는 보통 이벤트 기반의 비동기 자바 스크립트와 마찬가지로 "누군가 이것을 가져 와서 결과와 함께이 함수를 호출한다. 고마워, 이제 imma return, 이제 무엇이든한다"고 말하지 않았다. click 이벤트 핸들러 함수를 입력하고, DOM 요소를 업데이트하고, 다른 함수를 호출하고, 다른 함수가 오랫동안 작동 한 다음 반환됩니다. 같은 DOM 요소를 업데이트 한 다음 초기 함수에서 반환하여 효과적으로 비 웁니다. 스택. 그런 다음 브라우저는 큐의 다음 메시지로 이동할 수 있습니다. 내부 "DOM- 돌연변이"유형 이벤트를 트리거하여 생성 한 메시지 일 수 있습니다.
브라우저 UI는 현재 실행중인 프레임이 완료 될 때까지 (기능이 반환 될 때까지) UI를 업데이트 할 수 없습니다. 개인적으로 저는 이것이 제한보다는 설계에 의한 것이라고 생각합니다.
setTimeout
그러면 왜 작동합니까? 그렇게하면 자체 프레임에서 장기 실행 함수에 대한 호출을 효과적으로 제거하고 window
컨텍스트 에서 나중에 실행되도록 예약 하여 자체적 으로 즉시 리턴 하고 메시지 큐가 다른 메시지를 처리 할 수 있도록합니다. 그리고 아이디어는 DOM에서 텍스트를 변경할 때 Javascript로 우리에 의해 트리거 된 UI "업데이트"메시지가 장기 실행 기능을 위해 대기중인 메시지보다 앞서서 UI 업데이트가 차단되기 전에 발생한다는 것입니다 오랫동안.
a) 장기 실행 기능 은 실행될 때 모든 것을 여전히 차단합니다. b) UI 업데이트가 실제로 메시지 큐에서 우선한다는 보장은 없습니다. 2018 년 6 월 Chrome 브라우저에서 값이 0
바이올린이 보여주는 문제를 "수정"하지는 않습니다. 실제로는 이것으로 조금 어지럽습니다. 장기 실행 함수가 "나중에"실행되도록 예약하기 전에 트리거가 실행되기 때문에 UI 업데이트 메시지를 큐에 대기시켜야한다는 것이 논리적으로 보입니다. 그러나 V8 엔진에 방해가 될 수있는 최적화가 있거나 아마도 내 이해가 부족할 수 있습니다.
자,을 사용할 때의 문제점은 setTimeout
무엇이며,이 특별한 경우에 더 나은 해결책은 무엇입니까?
우선, setTimeout
다른 문제를 완화하기 위해 이와 같은 이벤트 핸들러에서 사용 하는 문제는 다른 코드와 혼동되기 쉽습니다. 내 작품의 실제 예는 다음과 같습니다.
이벤트 루프에 대해 잘못 이해 한 동료는 템플릿 렌더링 코드를 렌더링에 사용 setTimeout 0
하여 Javascript를 "스레딩"하려고했습니다 . 그는 더 이상 여기에 묻지 않지만 렌더링 속도를 측정하기 위해 타이머를 삽입했다고 가정 할 수 있습니다 (기능의 즉각적인 반환).이 접근 방식을 사용하면 해당 기능에서 빠른 응답을 할 수 있음을 알았습니다.
첫 번째 문제는 명백하다. 자바 스크립트를 스레드 할 수 없으므로 난독 화를 추가하는 동안 아무것도 얻지 못합니다. 두 번째로, 템플릿 렌더링이 예상되지 않았을 가능성이있는 가능한 이벤트 리스너 스택에서 템플릿 렌더링을 효과적으로 분리했습니다. 이 함수의 실제 동작은 이제 무의식적으로 함수를 실행하거나 함수에 의존하는 함수와 마찬가지로 결정적이지 않았습니다. 교육받은 추측을 할 수는 있지만 그 행동을 제대로 코딩 할 수는 없습니다.
논리에 의존하는 새로운 이벤트 핸들러를 작성할 때 "수정" 도 사용했습니다 setTimeout 0
. 그러나 이것은 수정이 아니며 이해하기 어렵고 이와 같은 코드로 인한 오류를 디버깅하는 것은 재미가 없습니다. 때로는 아무런 문제가 없으며 다른 경우에는 일관되게 실패하고 다시 플랫폼의 현재 성능과 당시 진행되는 다른 일에 따라 때때로 작동하고 중단되는 경우가 있습니다. 나는 개인적으로이 해킹을 (그 사용에 대해 조언을 할 이유입니다 입니다 당신이 정말 당신이 무슨 일을하는지 알고 결과가 무엇인가하지 않으면, 해킹, 우리는 모두 그것이 알고 있어야합니다).
그러나 우리는 대신 무엇을 할 수 있습니까? 참조 된 MDN 기사에서 알 수 있듯이 작업이 여러 메시지로 분할되면 (가능한 경우) 대기중인 다른 메시지가 작업과 인터리브되어 실행 중에 실행될 수 있으며, 웹 워커를 사용할 수 있습니다. 페이지와 함께 계산이 완료되면 결과를 반환합니다.
오, 그리고 만약 당신이 생각한다면, "음, 비 동기화하기 위해 장기 실행 함수에 콜백을 넣을 수 없었습니까?" 콜백은 비동기식으로 만들지 않으므로 명시 적으로 콜백을 호출하기 전에 장기 실행 코드를 실행해야합니다.