JavaScript는 모든 최신 브라우저 구현에서 단일 스레드로 알려져 있지만 표준에 지정되어 있습니까? 아니면 전통에 따라 지정 되었습니까? JavaScript가 항상 단일 스레드라고 가정하는 것이 안전합니까?
JavaScript는 모든 최신 브라우저 구현에서 단일 스레드로 알려져 있지만 표준에 지정되어 있습니까? 아니면 전통에 따라 지정 되었습니까? JavaScript가 항상 단일 스레드라고 가정하는 것이 안전합니까?
답변:
그건 좋은 질문이야. 나는“예”라고 말하고 싶습니다. 난 못해
JavaScript는 일반적으로 단일 실행 스레드가 스크립트 (*)에 표시되는 것으로 간주되므로 인라인 스크립트, 이벤트 리스너 또는 시간 초과가 입력 될 때 블록 또는 함수의 끝에서 돌아올 때까지 완전히 제어 할 수 있습니다.
(* : 브라우저가 하나의 OS 스레드를 사용하여 JS 엔진을 실제로 구현하는지 또는 WebWorkers가 다른 제한된 실행 스레드를 도입하는지에 대한 질문은 무시합니다.)
그러나 실제로 는 비열한 방식으로 이것은 사실이 아닙니다 .
가장 일반적인 경우는 즉각적인 이벤트입니다. 코드에서 무언가를 유발할 때 브라우저는 즉시이를 실행합니다.
var l= document.getElementById('log');
var i= document.getElementById('inp');
i.onblur= function() {
l.value+= 'blur\n';
};
setTimeout(function() {
l.value+= 'log in\n';
l.focus();
l.value+= 'log out\n';
}, 100);
i.focus();
<textarea id="log" rows="20" cols="40"></textarea>
<input id="inp">
log in, blur, log out
IE를 제외한 모든 결과가 나타 납니다. 이러한 이벤트는 사용자가 focus()
직접 전화했기 때문에 발생하는 것이 아니라 전화 alert()
를 걸거나 팝업 창을 열었거나 포커스를 이동시키는 다른 모든 것 때문에 발생할 수 있습니다 .
다른 이벤트가 발생할 수도 있습니다. 예를 들어 추가 i.onchange
전과 입력 청취자를 입력 뭔가를 focus()
호출을 unfocuses 및 로그 순서는 log in, change, blur, log out
그것이 어디 오페라를 제외 log in, blur, log out, change
하고 IE 곳의 (더 적은 explicably) log in, change, log out, blur
.
마찬가지로 click()
제공하는 요소를 호출하면 onclick
모든 브라우저에서 즉시 핸들러를 호출 합니다 (적어도 일관성이 있습니다!).
( on...
여기 에서 직접 이벤트 핸들러 속성을 사용하고 있지만 addEventListener
및 에서도 마찬가지 attachEvent
입니다.)
도발하기 위해 아무것도하지 않았 음에도 불구하고 코드가 스레드되는 동안 이벤트가 발생할 수있는 상황이 많이 있습니다 . 예를 들면 :
var l= document.getElementById('log');
document.getElementById('act').onclick= function() {
l.value+= 'alert in\n';
alert('alert!');
l.value+= 'alert out\n';
};
window.onresize= function() {
l.value+= 'resize\n';
};
<textarea id="log" rows="20" cols="40"></textarea>
<button id="act">alert</button>
히트 alert
하면 모달 대화 상자가 나타납니다. 대화를 취소 할 때까지 더 이상 스크립트가 실행되지 않습니다. 아니. 메인 창의 크기를 조정하면 alert in, resize, alert out
텍스트 영역에 나타납니다.
모달 대화 상자가 열려있는 동안 창의 크기를 조정하는 것이 불가능하다고 생각할 수도 있지만 그렇지 않습니다. Linux에서는 원하는만큼 창의 크기를 조정할 수 있습니다. Windows에서는 그렇게 쉽지는 않지만 화면 해상도가 창에 맞지 않는 더 큰 화면에서 더 작은 화면으로 변경하면 화면 크기가 조정됩니다.
스크립트가 스레드되어 있기 때문에 사용자가 브라우저와 활발한 상호 작용을하지 않을 때 발생할 수 resize
있는 것은 유일하다고 생각할 수도 있습니다 scroll
. 그리고 단일 창문의 경우 당신이 옳을 수도 있습니다. 그러나 크로스 윈도우 스크립팅을 수행하는 즉시 모든 기능이 작동합니다. 사용 중일 때 모든 창 / 탭 / 프레임을 차단하는 Safari 이외의 모든 브라우저의 경우 별도의 실행 스레드에서 실행되고 관련 이벤트 처리기가 발생하도록 다른 문서의 코드에서 문서와 상호 작용할 수 있습니다. 불.
스크립트가 여전히 스레드되는 동안 생성 될 수있는 이벤트가 발생할 수있는 위치는 다음과 같습니다.
모달 팝업은 (시 alert
, confirm
, prompt
), 열려있는 모든 브라우저하지만 오페라에있다;
시 showModalDialog
를 지원하는 브라우저에;
스크립트를 계속 실행하도록 선택하더라도 "이 페이지의 스크립트가 사용 중일 수 있습니다 ..."대화 상자가 표시됩니다. 스크립트가 중간에있는 동안에도 크기 조정 및 흐림 효과와 같은 이벤트가 발생하고 처리됩니다. Opera를 제외하고 사용 중입니다.
얼마 전에 필자는 Sun Java Plugin을 사용하는 IE에서 애플릿의 메소드를 호출하면 이벤트가 발생하고 스크립트를 다시 입력 할 수 있습니다. 이것은 항상 타이밍에 민감한 버그였으며 썬이 그 이후로 수정했을 가능성이 있습니다 (확실히 바랍니다).
아마 더. 이것을 테스트 한 지 오래되었습니다. 브라우저는 그 이후로 복잡해졌습니다.
요약하자면, 대부분의 사용자에게 JavaScript는 대부분 이벤트 중심의 단일 실행 스레드를 갖는 것으로 보입니다. 실제로는 그런 것이 없습니다. 이 중 얼마나 많은 부분이 단순히 버그인지, 고의적 인 디자인인지는 확실하지 않지만, 복잡한 응용 프로그램, 특히 크로스 윈도우 / 프레임 스크립팅 응용 프로그램을 작성하는 경우, 당신을 물릴 수있는 모든 기회가 있습니다. 디버그하기 어려운 방법.
최악의 상황이 최악의 경우 모든 이벤트 응답을 간접적으로 지정하여 동시성 문제를 해결할 수 있습니다. 이벤트가 들어 오면 이벤트를 큐에 버리고 나중에 setInterval
함수 에서 큐를 순서대로 처리하십시오 . 복잡한 응용 프로그램에서 사용하려는 프레임 워크를 작성하는 경우이 작업을 수행하는 것이 좋습니다. postMessage
또한 앞으로 크로스 문서 스크립팅의 고통을 진정으로 달래줄 것입니다.
blur
후 코드를 반환 브라우저로 제어 할 수 있습니다.
브라우저의 자바 스크립트 엔진이 비동기 적으로 실행하면 거의 모든 기존 (사소하지 않은) 자바 스크립트 코드가 작동하지 않기 때문에 예라고 대답하고 싶습니다.
HTML5가 이미 멀티 스레딩을 기본 자바 스크립트에 도입하는 웹 워커 (멀티 스레딩 자바 스크립트 코드를위한 명시적이고 표준화 된 API)를 지정 한다는 사실 은 무의미합니다.
( 다른 사람들의 덧글에 대한 참고 : 비록 setTimeout/setInterval
하나 - - 그들은 여전히 하나의 타임 라인을 따라 실행, HTTP 요청은 이벤트 (XHR), 및 UI 이벤트 (클릭, 초점 등) 멀티 쓰레드 특성의 조잡한 느낌을 제공을 온로드 시간-따라서 사전에 실행 순서를 모르더라도 이벤트 핸들러, 시간 함수 또는 XHR 콜백을 실행하는 동안 외부 조건이 변경되는 것에 대해 걱정할 필요가 없습니다.)
나는 스펙이 누군가가 여러 스레드에서 자바 스크립트 를 실행 하는 엔진 을 생성하는 것을 막지 못한다고 말할 것입니다. 에서 공유 객체 상태에 액세스하기 위해 코드를 동기화해야합니다.
단일 스레드 비 차단 패러다임 은 브라우저 에서 자바 스크립트를 실행할 필요가 있다고 생각 합니다. ui가 차단해서는 안되는 에서 .
Nodejs 는 브라우저의 접근 방식을 따랐습니다. .
그러나 Rhino 엔진 은 다른 스레드에서 js 코드 실행을 지원합니다 . 실행은 컨텍스트를 공유 할 수 없지만 범위를 공유 할 수 있습니다. 이 특정 사례의 경우 설명서에 다음과 같이 명시되어 있습니다.
... "Rhino는 JavaScript 객체의 속성에 대한 액세스가 스레드 전체에서 원자 성임을 보장하지만 동일한 범위에서 동시에 동일한 범위에서 실행되는 스크립트를 더 이상 보장하지 않습니다. 두 스크립트가 동일한 범위를 동시에 사용 하면 스크립트는 공유 변수에 대한 액세스 조정을 담당합니다 . "
Rhino 문서를 읽음으로써 누군가가 새로운 자바 스크립트 스레드를 생성하는 자바 스크립트 API를 작성할 수 있지만, API는 코뿔소에 따라 다릅니다 (예 : 노드는 새로운 프로세스 만 스폰 할 수 있음).
자바 스크립트에서 여러 스레드를 지원하는 엔진의 경우에도 멀티 스레딩 또는 차단을 고려하지 않는 스크립트와의 호환성이 있어야한다고 생각합니다.
Concearning 브라우저를 하고 nodejs 방법 나는 그것이 참조 :
따라서 브라우저 및 nodejs (및 아마도 다른 많은 엔진)의 경우 javascript는 다중 스레드가 아니지만 엔진 자체는 입니다.
웹 작업자의 존재는 누군가가 별도의 스레드에서 실행될 자바 스크립트로 코드를 만들 수 있다는 점에서 자바 스크립트가 멀티 스레드 될 수 있음을 더 정당화합니다.
그러나 웹 작업자는 실행 컨텍스트를 공유 할 수 있는 기존 스레드의 문제를 해결하지 않습니다 . 위의 규칙 2와 3은 여전히 적용됩니다 되지만 이번에는 스레드 코드가 사용자 (js 코드 작성기)가 javascript로 작성됩니다.
고려해야 할 유일한 것은 효율성 (및 동시성이 아닌) 에서 생성 된 스레드 수입니다. 아님) 관점 . 아래를보십시오 :
Worker 인터페이스는 실제 OS 수준 스레드를 생성하며, 신중한 프로그래머는주의하지 않으면 동시성이 코드에 "흥미로운"영향을 줄 수 있다고 우려 할 수 있습니다.
그러나 웹 작업자는 다른 스레드와의 통신 지점 을 신중하게 제어 하므로 실제로 동시성 문제를 일으키는 것은 매우 어렵습니다 . 스레드로부터 안전하지 않은 구성 요소 나 DOM에 액세스 할 수 없습니다. 그리고 직렬화 된 객체를 통해 특정 데이터를 스레드 안팎으로 전달해야합니다. 따라서 코드에 문제를 일으키려면 정말 열심히 노력해야합니다.
JavaScript / ECMAScript는 호스트 환경 내에서 작동하도록 설계되었습니다. 즉, 호스트 환경이 주어진 스크립트를 구문 분석하고 실행하기로 결정하고 JavaScript가 실제로 유용한 브라우저 (예 : 브라우저의 DOM)가 될 수있는 환경 객체를 제공하지 않는 한 JavaScript는 실제로 아무것도 하지 않습니다 .
주어진 함수 또는 스크립트 블록이 한 줄씩 실행되며 JavaScript에서 보장된다고 생각합니다. 그러나 호스트 환경은 동시에 여러 스크립트를 실행할 수 있습니다. 또는 호스트 환경은 항상 멀티 스레딩을 제공하는 객체를 제공 할 수 있습니다. setTimeout
그리고 setInterval
예, 또는 적어도 의사 예에서, 호스트 환경의 (정확히 동시성 아니더라도) 일부 병행 할 수있는 방법을 제공한다.
아니.
나는 여기 군중들과 맞서고 있지만 나와 함께 견뎌내십시오. 단일 JS 스크립트는 효과적으로 단일 스레드로 만들어졌지만 이것이 다르게 해석 될 수 없음을 의미하지는 않습니다.
다음 코드가 있다고 가정 해 봅시다 ...
var list = [];
for (var i = 0; i < 10000; i++) {
list[i] = i * i;
}
이것은 루프가 끝날 때까지 목록에 인덱스 제곱 인 10000 개의 항목이 있어야한다고 기대하지만 VM은 루프의 각 반복이 다른 루프에 영향을 미치지 않으며 두 개의 스레드를 사용하여 재 해석 할 수 있음을 알 수 있습니다.
첫 번째 실
for (var i = 0; i < 5000; i++) {
list[i] = i * i;
}
두 번째 실
for (var i = 5000; i < 10000; i++) {
list[i] = i * i;
}
JS 배열이 멍청한 메모리 청크보다 복잡하기 때문에 여기서 단순화하고 있습니다. 그러나이 두 스크립트가 스레드 안전 방식으로 배열에 항목을 추가 할 수 있다면 두 가지가 모두 실행될 때까지 단일 스레드 버전과 동일한 결과입니다.
이와 같은 병렬화 가능한 코드를 감지하는 VM을 모르지만 JIT VM의 경우 향후에 더 빠른 속도를 제공 할 수 있기 때문에 향후에 존재할 가능성이 높습니다.
이 개념을 더 발전 시키면 VM에 멀티 스레드 코드로 변환 할 항목을 알려주기 위해 코드에 주석을 달 수 있습니다.
// like "use strict" this enables certain features on compatible VMs.
"use parallel";
var list = [];
// This string, which has no effect on incompatible VMs, enables threading on
// this loop.
"parallel for";
for (var i = 0; i < 10000; i++) {
list[i] = i * i;
}
웹 워커가 Javascript를 사용하고 있기 때문에이 시스템이 더 못 생겨 질 가능성은 없지만 Javascript가 전통적으로 단일 스레드라고 말하는 것이 안전하다고 생각합니다.
@bobince의 예제를 약간 수정하여 시도했습니다.
<html>
<head>
<title>Test</title>
</head>
<body>
<textarea id="log" rows="20" cols="40"></textarea>
<br />
<button id="act">Run</button>
<script type="text/javascript">
let l= document.getElementById('log');
let b = document.getElementById('act');
let s = 0;
b.addEventListener('click', function() {
l.value += 'click begin\n';
s = 10;
let s2 = s;
alert('alert!');
s = s + s2;
l.value += 'click end\n';
l.value += `result = ${s}, should be ${s2 + s2}\n`;
l.value += '----------\n';
});
window.addEventListener('resize', function() {
if (s === 10) {
s = 5;
}
l.value+= 'resize\n';
});
</script>
</body>
</html>
따라서 실행을 누르고 경고 팝업을 닫고 "단일 스레드"를 수행하면 다음과 같은 내용이 표시됩니다.
click begin
click end
result = 20, should be 20
그러나 Windows에서 Opera 또는 Firefox에서 안정적으로 실행하고 화면에 경고 팝업이있는 창을 최소화 / 최대화하면 다음과 같은 결과가 나타납니다.
click begin
resize
click end
result = 15, should be 20
나는 이것이 "멀티 스레딩"이라고 말하고 싶지 않지만 이것을 예상하지 못한 코드 조각이 잘못된 시간에 실행되어 현재 상태가 손상되었습니다. 그리고이 행동에 대해 아는 것이 좋습니다.
두 개의 setTimeout 함수를 서로 중첩 시키려고하면 멀티 스레드로 작동합니다 (즉, 외부 타이머는 기능을 실행하기 전에 내부 타이머가 완료 될 때까지 기다리지 않습니다).
setTimeout(function(){setTimeout(function(){console.log('i herd you liek async')}, 0); alert('yo dawg!')}, 0)
(기록을 위해 yo dawg는 항상 먼저 나와야하며 콘솔 로그 출력)