JavaScript 단일 스레드
JavaScript의 단일 스레딩은 그 목적과 관련이 있습니다. 브라우저 스크립팅 언어로서 JavaScript의 주요 목적은 사용자와 상호 작용하고 DOM을 운영하는 것입니다. 이것은 단일 스레드 일 수 있다고 결정합니다. 그렇지 않으면 매우 복잡한 동기화 문제가 발생합니다. 예를 들어, JavaScript가 두 개의 스레드가 동시에 두 개의 스레드가 있고, 하나의 스레드가 특정 DOM 노드에 컨텐츠를 추가하고, 다른 스레드는이 노드를 삭제합니다. 따라서 복잡성을 피하기 위해 JavaScript는 출생의 단일 스레드이며,이 언어의 핵심 특징이되어 향후 변하지 않을 것입니다.
대기열 작업
단일 스레딩은 모든 작업을 대기해야하며 다음 작업이 실행되기 전에 이전 작업이 실행됩니다. 이전 작업에 시간이 오래 걸리면 다음 작업이 기다려야합니다.
비동기 이벤트 드라이버
마우스 클릭 이벤트, 창 크기 드래그 이벤트, 타이머 트리거 이벤트, XMLHTTPREQUEST 완료 콜백 등 브라우저의 많은 동작은 비동기 이벤트가 발생하면 이벤트 큐에 들어갑니다. 브라우저에는 내부 대형 메시지 루프, 이벤트 루프가있어 대형 이벤트 대기열 및 프로세스 이벤트가 설비됩니다. 예를 들어, 브라우저는 현재 OnClick 이벤트를 처리하는 데 바쁘고 다른 이벤트가 발생합니다 (예 : Window Onsize)이 발생 하며이 비동기 이벤트는 이벤트 큐에 넣어 처리를 기다립니다. 이 이벤트는 이전 처리가 완료되고 무료 일 때만 실행됩니다.
이벤트 루프
JavaScript는 단일 스레드이지만 브라우저는 단일 스레드가 아닙니다
브라우저에는 다음 프로세스 중 일부가 있습니다.
1. 브라우저 GUI 렌더링 스레드
2. JavaScript 엔진 스레드
3. 브라우저 타이밍 트리거 스레드
4. 브라우저 이벤트는 스레드를 트리거합니다
5. 브라우저 HTTP 비동기 요청 스레드
JavaScript 엔진은 단일 스레드이므로 코드는 먼저 큐에 눌린 다음 엔진으로 최초의 첫 번째 방식으로 실행됩니다. 이벤트 처리 기능 및 타이머 실행 기능 도이 대기열에 배치 된 다음 무한 루프를 사용하여 팀의 헤드에서 기능을 지속적으로 추출하여 실행합니다. 이것은 이벤트 루프입니다.
요약하면 JS는 단일 스레드이지만 브라우저는 다중 스레드입니다. 비동기식 물건을 만나면 브라우저는 비동기 콜백을 이벤트 루프에 넣습니다. JS 스레드가 바쁘지 않으면 이벤트 루프를 읽으십시오.
타이머 원리
타이머 사용 방법
settimeout (FN, 지연)
SetInterval (FN, 지연)
FN은 함수 또는 문자열이며 지연은 지연 시간이며 단위는 밀리 초입니다.
주목해야 할 사항이 있습니다
1. FN은 문자열 일 수 있지만 이렇게 사용하는 것이 권장되지 않습니다.
2. FN 에이 기능이 있으면 실행할 때 창을 가리 킵니다.
JS 단일 스레드와 이벤트 루프를 잘 이해하면 타이머의 원리를 이해하기 쉽습니다.
타이머가 설정되면 지연 시간에 도달하면 브라우저가 지연 실행 이벤트를 이벤트 루프에 넣습니다. 시간이 다가 오면 JS 스레드가 유휴 상태 인 경우 실행됩니다 (따라서 타이머의 정확도가 정확하지 않음)
항상 기능을 설문 조사하는 Settimeout과 SetInterval의 차이점에 대한 기사를 읽었습니다. 코드는 다음과 같습니다
코드 사본은 다음과 같습니다.
settimeout (function () {
settimeout (arguments.callee, 100)
}, 100)
setInterval (function () {}, 1000)
기사의 일반적인 의미는 콜백 함수가 실행 된 후 다음 타이머를 시작한다는 것입니다. 따라서 SetInterval은 항상 실행되는 동안 간격으로 실행해야합니다. 계속 실행되는 JS 스레드가 발생하면 이벤트 루프에 여러 콜백을 추가 할 수 있습니다. JS 스레드가 바쁘지 않으면 여러 번의 실행이 진행됩니다.
테스트 후, SetInterval은 IE, FF, Chrome, Opera 및 Safari에 관계없이 특정 간격이라는 것이 밝혀졌습니다.
테스트 코드는 다음과 같습니다
코드 사본은 다음과 같습니다.
setInterval (function () {
xx.innerhtml = xx.innerhtml+1;
}, 100);
for (var i = 0; i <6000000; i ++) {
xx.offsetwidth
}
settimeout (function () {
디버거;
}, 10)
브레이크 포인트가 여전히 인쇄 된 경우 1 만 인쇄합니다
타이머 정확도 문제
JS 단일 스레드로 인해 바쁘게되면 타이머는 확실히 부정확 할 것이며 확실히 길고 길어질 것입니다. 이것은 해결할 수없고 해결책이없는 것 같습니다.
또 다른 정확도 문제는 최소 간격 설정 타임 아웃입니다 (재미, 0)
JS 스레드가 바쁘지 않으면 0 초 직후에 실행할 수 없습니다. 항상 최소 간격이 있으며 각 브라우저는 여전히 다릅니다. 이것은 테스트되지 않았습니다.
W3C의 표준에 관한 기사를 읽었습니다. 타이머의 최소 시간 실행은 4ms입니다. 찾을 수없는 경우 소스를 확인할 방법이 없습니다! ! !
타이머와 관련된 일부 최적화
타이머를 만들 때 여전히 약간의 최적화가 있습니다
예를 들어, Window.onresize를 바인딩하면 브라우저가 확대되면 트리거가 매우 자주 발생하므로 실행을 지연시킬 수 있습니다. 다음 실행이 지워지면 빈번한 실행이 줄어 듭니다.
의사 코드는 다음과 같습니다
코드 사본은 다음과 같습니다.
var 타이머;
함수 r () {
클리어 타임 아웃 (타이머);
타이머 = settimeout (function () {
// 무언가를합니다
}, 150);
}
2. 스크롤 막대가 아래로 당겨지면 약간도 있습니다. 예를 들어, 그림의 lazyload에는 과도한 계산을 피하기위한 타이머가 있어야합니다.
3. 타이머가 필요한 곳이 여러 개 있으면 타이머로 병합 할 수 있습니다. 시간 간격이 가장 작은 시간 간격입니다. 그런 다음 실행 해야하는 콜백 함수가 배열로 채워집니다. 시간 간격에 도달하면 배열을 통해 반복하여 실행하십시오.
작은 데모
코드 사본은 다음과 같습니다.
<! doctype html>
<html>
<헤드>
<meta charset = "utf-8" />
<스타일>
.wrap {너비 : 80%; 마진 : 30px 자동; 국경 : 1px Solid #CCC; 패딩 : 20px;}
.c {테두리 : 1px 고체 #ccc; 높이 : 30px; 마진 바닥 : 20px;}
</스타일>
</head>
<body>
<div id = "xx"> </div>
<div>
<div id = "a1"> 0 </div>
<div id = "a2"> 0 </div>
<div id = "a3"> 0 </div>
<div id = "a4"> 0 </div>
</div>
<script src = "http://static.paipaiimg.com/paipai_h5/js/ttj/zepto.min.js"> </script>
<script type = "text/javaScript">
var 런타임 = {
옵션 : {
단계 : 1000
},
콜백 : [],
AddCallbacks : [],
시작 : 거짓,
타이머 : NULL,
확장 : function () {
var target = 인수 [0] || {}, i = 1, length = arguments.length, 옵션;
if (typeof target! = "object"&& typeof target! = "function")
대상 = {};
for (; i <길이; i ++)
if ((옵션 = 인수 [i])! = null)
for (옵션의 var name) {
var copy = 옵션 [이름];
if (target === Copy)
계속하다;
if (copy! == 정의되지 않은)
대상 [이름] = 복사;
}
반환 대상;
},
init : 함수 (옵션) {
$ .extend (this, this.options, 옵션 || {});
},
추가 : 기능 (재미, 옵션) {
옵션 = 옵션 || {};
this.addcallbacks.push ({{
재미 : 재미,
STARTTIME : New Date (). gettime (),
단계 : 옵션 .Step || 이. 스텝,
I : 1
});
var self = 이것;
if (! this.start) {
this.callbacks = [재미];
this.start = true;
this.startTime = 새 날짜 (). gettime ();
this.timer = setInterval (function () {
self.done ();
}, this.step);
}
},
완료 : function () {
var 콜백 = this.callbacks,
self = this,
newarr = [];
$ .Each (콜백, 기능 (i, obj) {
if (obj.step == self.step) {
obj.fun ();
}또 다른{
if (obj.i == obj.step/self.step) {
if ((새 날짜 (). gettime ())-obj.starttime> obj.step*2/3) {
obj.fun ();
}
obj.i = 1;
}또 다른{
obj.i = obj.i + 1;
}
}
});
$ .Each (this.addcallbacks, function (i, obj) {
if (obj.step == self.step) {
if ((새 날짜 (). gettime ())-obj.starttime> obj.step*2/3) {
obj.fun ();
Callbacks.push (obj);
}또 다른{
newarr.push (obj);
}
}또 다른{
obj.i = obj.i + 1;
Callbacks.push (obj);
}
});
this.addcallbacks = newarr;
},
Clear : function () {
ClearInterval (this.Timer);
}
}
runtime.init ();
runtime.add (function () {
a1.innerhtml = ~~ a1.innerhtml+1;
});
runtime.add (function () {
a2.innerhtml = ~~ a2.innerhtml+1;
}, {단계 : 2000});
runtime.add (function () {
a3.innerhtml = ~~ a3.innerhtml+1;
}, {단계 : 4000});
runtime.add (function () {
a4.innerhtml = ~~ a4.innerhtml+1;
}, {단계 : 8000});
</스크립트>
</body>
</html>
JavaScript 타이머에 대해 알고 있습니까? 궁금한 점이 있으면 메시지를 남겨주세요.