>웹 프론트엔드 >JS 튜토리얼 >JavaScript multi-threading_javascript 기술 구현 방법

JavaScript multi-threading_javascript 기술 구현 방법

WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB
WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB원래의
2016-05-16 19:13:421260검색

참고: 다음 내용은 IE의 GIF onload 이벤트를 기반으로 하므로 IE의 모든 테스트는
몇 장의 사진을 사용해야 합니다

먼저 간단한 사실을 살펴보겠습니다.

코드 복사 코드는 다음과 같습니다.




예상대로 IE에 경고 프롬프트가 많이 표시됩니다. 창문. "많다"라는 점에 유의하세요!

이유는 매우 간단합니다. IE는 멀티 프레임 GIF의 onload 이벤트를 반복적으로 실행합니다. 즉, 애니메이션이 재생될 때마다 onload 이벤트가 다시 실행됩니다.

(참고: ESC 키를 누르면 gif 애니메이션 재생이 중지되므로 onload 이벤트 실행도 중지됩니다.)

이 기능을 사용하면 다음의 구현을 시뮬레이션할 수 있습니다. 멀티스레딩:
아래 코드 참조:


코드 복사 코드는 다음과 같습니다.
이미지 A 온로드 실행 횟수 :0

이미지 B 온로드 실행 횟수: 0

이미지 C 온로드 실행 시간: 0
<script> <br>function Img(threadID, src) <br>{ <br>var img = new Image; <br>img.onload = function() <br>{ <br>var c =parseInt(document.getElementById(threadID).innerHTML); document.getElementById(threadID).innerHTML=isNaN(c) ?1: c; <br>} <br>img.src = <br>return img; new Img("ThreadA","attachment/1178365293_0.gif "); <br>var imgB = new Img("ThreadB","attachment/1178365293_1.gif") <br>var imgC = new Img("ThreadC" ,"attachment/1178365293_2.gif"); <br> </script>



재밌지 않나요?
다음 코드를 다시 보세요.




코드 복사
코드는 다음과 같습니다. <script> </a>//by Rimifon </span>var Threads = new Array; </div>onload = function() <div class="codebody" id="code30862">{ <br>for(var C=1;C<501 ;C ) <br>{ <br>Threads.push(new Thread(C)) <br>} <br>} <br>function Go(sender) <br>{ <br>var IsStart = sender.value =="모두 시작"; <br>for(var x in Threads) <br>{ <br>Threads[x].Start = IsStart; <br>} <br>sender.value = "All"(IsStart? "일시 중지":" 시작"); <br>} <br>function Thread(ID) <br>{ <br>this.Start = 0; <br>varcursor=<br>varspan=document. createElement("span" ); <br>var counter = document.createTextNode("0"); <br>span.appendChild(counter) <br>var div = document.createElement("div"); div.appendChild(document .createTextNode("스레드" ID ":")); <br>div.style.cursor = "포인터"; <br>div.onclick = function() <br>{ <br>cursor. 시작 = !cursor.Start; <br>} <br>div.oncontextmenu = function() <br>{ <br>img.onload = null; <br>this.removeNode(true); <br>} <br>div.appendChild(span); <br>document.body.appendChild(div); <br>var img = new Image; <br>img.onload = function() <br> 🎜>if(cursor .Start) counter.data =parseInt(counter.data) 1; <br>div.style.BackgroundColor =cursor.Start?"#abcdef":"yellow" <br>} <br>img .src = "images /51js.gif"; <br>} <br></script>
><입력 유형 =버튼 값="팝업 대화 상자" onclick="alert('대화 상자 테스트')">
코드의 일부는 리미폰에서 인용했습니다. (http://Dnew.cn에서 재인쇄)
JavaScript 멀티 스레드 프로그래밍 소개
AJAX 기술을 사용하여 점점 더 많은 웹사이트가 개발되고 있지만 복잡한 AJAX 애플리케이션을 구축하는 것은 여전히 ​​어려운 일입니다. 어려운 문제. 이러한 어려움의 주된 이유는 무엇입니까? 서버와의 비동기 통신 문제인가요? 아니면 GUI 프로그래밍 문제입니까? 일반적으로 이 두 가지 작업은 데스크톱 프로그램으로 완료되는데, 동일한 기능을 수행할 수 있는 AJAX 애플리케이션을 개발하는 것이 왜 그렇게 어려운가요?

AJAX 개발의 어려움
간단한 예를 통해 이 문제를 이해해 보겠습니다. 모든 기사 정보를 서버에서 한꺼번에 불러오는 것이 아니라, 사용자 요청에 따라 서버와 상호 작용하여 각 기사의 정보를 동적으로 불러올 수 있는 트리 구조의 게시판 시스템(BBS)을 구축한다고 가정해 보겠습니다. 각 기사에는 시스템에서 고유 식별자로 사용할 수 있는 ID, 포스터 이름, 기사 내용, 모든 하위 기사의 ID를 포함하는 배열 정보 등 4가지 관련 속성이 있습니다. 먼저, 기사 정보를 불러올 수 있는 getArticle()이라는 함수가 있다고 가정합니다. 이 함수가 전달받은 인자는 로딩할 기사의 ID이며, 이를 통해 서버로부터 기사 정보를 얻을 수 있다. 반환되는 객체에는 기사와 관련된 4가지 속성(id, name, content 및 children)이 포함되어 있습니다. 루틴은 다음과 같습니다.


코드 복사 코드는 다음과 같습니다.

function ( id ) {
var a = getArticle(id);
document.writeln(a.name "" a.content)
}

그러나 , 동일한 기사 ID로 이 함수를 반복적으로 호출하면 서버와의 반복적이고 도움이 되지 않는 통신이 필요하다는 것을 알 수 있습니다. 이 문제를 해결하려면 캐싱 기능이 있는 getArticle()과 동일한 getArticleWithCache() 함수 사용을 고려할 수 있습니다. 이 예에서 getArticle()에서 반환된 데이터는 전역 변수로만 저장됩니다.
코드 복사 코드는 다음과 같습니다. :

var 캐시 = {};
function getArticleWithCache (id) {
if ( !cache[id] ) {
cache[id] = getArticle(id);
}
return 캐시[id];
}

이제 읽은 기사가 캐시되었으므로 앞서 언급한 기능을 사용하는 backgroundLoad() 함수를 다시 고려해 보겠습니다. 위의 캐싱 메커니즘은 모든 기사 정보를 로드합니다. 그 목적은 독자가 기사를 읽을 때 배경에서 모든 하위 기사를 미리 로드하는 것입니다. 기사 데이터가 트리 구조로 되어 있기 때문에 트리를 순회하고 모든 기사를 로드하는 재귀 알고리즘을 쉽게 작성할 수 있습니다.
코드 복사 코드는 다음과 같습니다.

function backgroundLoad ( ids ) {
for ( var i=0; i < ids.length; i ) {
var a = getArticleWithCache(ids[ i]);
backgroundLoad(a.children)
}
}

backgroundLoad() 함수는 ID 배열을 매개변수로 받은 후, 각 ID를 통해 앞서 정의한 getArticldWithCache() 메소드를 호출하여 각 ID에 해당하는 기사를 캐싱합니다. 그런 다음 로드된 아티클의 하위 아티클 ID 배열을 통해 backgroundLoad() 메서드가 재귀적으로 호출되어 전체 아티클 트리가 캐시됩니다.

지금까지는 모든 것이 완벽해 보이는 것 같습니다. 그러나 AJAX 애플리케이션 개발 경험이 있는 한 이 단순한 구현 방법은 전혀 성공하지 못할 것이라는 점을 알아야 합니다. 이 예제의 기본은 getArticle()이 기본적으로 동기 통신을 사용한다는 것입니다. 그러나 기본적으로 JavaScript는 단일 스레드이기 때문에 서버와 상호 작용할 때 비동기 통신이 필요합니다. 단순성 측면에서 모든 것(GUI 이벤트 및 렌더링 포함)을 스레드에 배치하는 것은 스레드 동기화의 복잡한 문제를 고려할 필요가 없기 때문에 좋은 프로그래밍 모델입니다. 한편, 그는 애플리케이션 개발에 있어서도 심각한 문제를 노출했습니다. 단일 스레드 환경은 사용자 요청에 빠르게 응답하는 것처럼 보이지만 스레드가 다른 작업(예: getArticle() 호출)을 처리하는 중일 때는 응답할 수 없습니다. 사용자의 마우스 클릭과 키보드 조작에 반응합니다.

이런 싱글스레드 환경에서 동기통신을 하면 어떻게 될까요? 동기 통신은 통신 결과를 얻을 때까지 브라우저 실행을 중단합니다. 통신 결과를 기다리는 동안 서버 호출이 아직 완료되지 않았으므로 스레드는 사용자에 대한 응답을 중지하고 호출이 반환될 때까지 잠금 상태를 유지합니다. 이로 인해 브라우저는 서버의 응답을 기다리는 동안 사용자 동작에 응답하지 못하여 정지된 것처럼 보입니다. getArticleWithCache() 및 backgroundLoad()를 실행할 때 동일한 문제가 발생합니다. 둘 다 getArticle() 함수를 기반으로 하기 때문입니다. 모든 기사를 다운로드하는 데 상당한 시간이 걸릴 수 있으므로 이 기간 동안 브라우저가 정지되는 것은 backgroundLoad() 함수에 심각한 문제입니다. 브라우저가 정지되었기 때문에 사용자가 기사를 읽을 때 백그라운드에서 데이터를 먼저 로드하면 현재 기사를 읽을 수도 없습니다.

위에서 언급한 것처럼 동기식 통신은 사용에 있어서 이렇게 심각한 문제를 일으킬 수 있기 때문에 자바스크립트에서는 비동기식 통신을 기본 원칙으로 삼고 있습니다. 따라서 위의 프로그램을 비동기 통신을 기반으로 다시 작성할 수 있습니다. JavaScript에서는 비동기 통신 프로그램을 작성하기 위해 이벤트 기반 프로그래밍 접근 방식이 필요합니다. 대부분의 경우 통신 응답이 수신되면 호출될 콜백 루틴을 지정해야 합니다. 예를 들어 위에 정의된 getArticleWithCache()는 다음과 같이 작성할 수 있습니다.
코드 복사 코드는 다음과 같습니다.

var 캐시 = {};
function getArticleWithCache (id, callback) {
if ( !cache[id] ) {
callback(cache[id])
} else {
getArticle (id, function( a ){
cache[id] = a;
callback(a);
})
}
}

이 프로그램도 내부적으로 getArticle() 함수를 호출합니다. 그러나 비동기 통신을 위해 설계된 이 버전의 getArticle() 함수는 함수를 두 번째 매개변수로 받아들인다는 점에 유의해야 합니다. getArticle() 함수를 호출하면 이전과 마찬가지로 서버에 요청이 전송됩니다. 차이점은 이제 서버의 응답을 기다리지 않고 함수가 빠르게 반환된다는 점입니다. 이는 실행이 호출 프로그램으로 반환될 때 서버로부터 응답이 없음을 의미합니다. 이런 방식으로 스레드는 서버로부터 응답을 받을 때까지 다른 작업을 수행할 수 있으며 이때 콜백 함수를 호출할 수 있습니다. 서버가 응답하면 getArticle()의 두 번째 매개변수는 미리 정의된 콜백 함수로 호출되며 서버의 반환 값은 해당 매개변수입니다. 마찬가지로 getArticleWithCache()도 일부 변경을 수행하고 콜백 매개변수를 두 번째 매개변수로 정의해야 합니다. 이 콜백 함수는 getArticle()에 전달된 콜백 함수에서 호출되므로 서버 통신이 완료된 후에 실행될 수 있습니다.

이미 위의 변경 사항이 꽤 복잡하다고 생각하실 수도 있지만, backgroundLoad() 함수에 대한 변경 사항은 더 복잡해지며 콜백 함수를 처리할 수 있는 형식으로 다시 작성해야 합니다.
코드 복사 코드는 다음과 같습니다.

function backgroundLoad ( ids, callback ) {
var i = 0;
function l ( ) {
if ( i < ids.length ) {
getArticleWithCache(ids[i ], function( a ){
backgroundLoad(a.children , l);
} else {
콜백()
}
}


수정된 backgroundLoad() 함수는 이전 함수와 매우 다르게 보이지만 구현하는 함수는 동일합니다. 이는 두 함수 모두 ID 배열을 매개변수로 받아들이고 배열의 각 요소에 대해 getArticleWithCache()를 호출한 다음 얻은 하위 기사 ID를 사용하여 backgroundLoad()를 재귀적으로 호출한다는 것을 의미합니다. 그러나 이전 프로그램에서는 for 루프 문으로 완료한 동일한 배열 순환 액세스를 식별하기 어렵습니다. 동일한 기능을 구현하는 두 가지 기능 세트가 왜 그렇게 다른가요?

이러한 차이점은 모든 함수가 getArticleWithCache()와 같이 서버와 통신해야 하는 상황이 발생한 후 즉시 반환해야 한다는 사실에서 비롯됩니다. 서버 응답을 받아야 하는 콜백 함수는 원래 함수가 실행되지 않는 한 호출할 수 없습니다. JavaScript를 사용하면 루프 중에 프로그램을 중단하고 for 문과 같이 나중에 이 중단점에서 실행을 다시 시작할 수 없습니다. 따라서 이 예제에서는 콜백 함수의 재귀 전달을 사용하여 기존 루프 문 대신 루프 구조를 구현합니다. CPS(Continuous Transfer Style)에 익숙한 사람들에게는 이것이 CPS의 수동 구현입니다. 루프 구문을 사용할 수 없기 때문에 앞서 언급한 트리 탐색과 같은 간단한 프로그램도 매우 복잡하게 작성해야 합니다. 이벤트 중심 프로그램 설계와 관련된 문제는 제어 흐름의 문제입니다. 루프 및 기타 제어 흐름 표현은 이해하기 어려울 수 있습니다.

또 다른 문제가 있습니다. 비동기 통신을 적용하지 않는 함수를 비동기 통신을 사용하는 함수로 변환하면 다시 작성된 함수에 새 매개변수로 콜백 함수가 필요하게 되는데, 이는 이미 기존 API에 있습니다. 내부 변경은 내부적으로 영향을 제한하지 않고 API와 다른 API 사용자의 전반적인 혼란을 초래하기 때문에 큰 문제를 야기합니다.

이러한 문제의 근본 원인은 무엇입니까? 예, 이러한 문제를 일으키는 것은 JavaScript의 단일 스레드 메커니즘입니다. 단일 스레드에서 비동기 통신을 수행하려면 이벤트 기반 프로그래밍과 복잡한 명령문이 필요합니다. 프로그램이 서버의 응답을 기다리는 동안 사용자 요청을 처리할 수 있는 다른 스레드가 있는 경우 위의 복잡한 기술은 필요하지 않습니다. <… 비동기 통신에. 이것은 JavaScript로 작성된 무료 소프트웨어 라이브러리입니다. 사용의 전제는 Mozilla Public License 및 GNU General Public License를 준수하는 것입니다. 해당 웹사이트에서 소스 코드를 다운로드할 수 있습니다.

지금 소스코드를 다운로드하여 사용해 보세요! 다운로드한 소스 코드를 Concurrent.Thread.js라는 폴더에 저장했다고 가정합니다. 작업을 수행하기 전에 다음 프로그램을 실행합니다.



코드 복사 코드는 다음과 같습니다.


이 프로그램을 실행하면 시작되는 숫자가 순차적으로 표시됩니다. 0부터 차례로 나타나며, 스크롤하여 보실 수 있습니다. 이제 코드를 자세히 살펴보겠습니다. 이는 결코 종료되지 않는 루프를 생성하기 위해 while(1) 조건을 사용합니다. 일반적으로 이와 같이 하나의 스레드만 계속 사용하는 JavaScript 프로그램은 브라우저가 정지되는 것처럼 보입니다. 동일하다면 당연히 스크롤이 허용되지 않습니다. 그러면 왜 위의 프로그램이 이것을 가능하게 합니까? 핵심은 while(1) 위의 Concurrent.Thread.create() 문입니다. 이는 이 라이브러리에서 제공하는 메서드로 새 스레드를 생성할 수 있습니다. 매개변수로 전달된 함수는 이 새 스레드에서 실행됩니다.



이 프로그램에는 숫자를 반복적으로 표시할 수 있는 새로운 함수 f()가 있습니다. 이 함수는 프로그램 세그먼트의 시작 부분에 정의되어 있으며 f()를 매개변수로 사용하여 create() 메서드가 두 번 호출됩니다. create() 메소드의 두 번째 매개변수는 변경되지 않고 f()에 전달됩니다. 이 프로그램을 실행하면 먼저 0부터 시작하는 일부 소수점, 100,000부터 시작하는 큰 숫자, 이전 소수점 순서를 따르는 숫자를 볼 수 있습니다. 프로그램이 소수와 큰 숫자를 번갈아 표시하는 것을 볼 수 있는데, 이는 두 개의 스레드가 동시에 실행되고 있음을 나타냅니다.

Concurrent.Thread의 또 다른 사용법을 보여드리겠습니다. 위의 예에서는 create() 메서드를 호출하여 새 스레드를 생성합니다. 라이브러리에서 API를 호출하지 않고도 이를 달성할 수 있습니다. 예를 들어 이전 예제는 다음과 같이 작성할 수 있습니다.
코드 복사 코드는 다음과 같습니다.


🎜>
in script 태그 내에는 단순히 자바스크립트로 무한 루프가 작성되어 있습니다. 태그에 유형 속성이 있음을 확인해야 합니다. (text/x-script.multithreaded-js) 이 속성이 스크립트 태그에 있으면 Concurrent.Thread가 새 스레드에 있습니다. 태그 사이의 프로그램. 이 경우와 마찬가지로 Concurrent.Thread 라이브러리가 포함되어야 한다는 점을 기억해야 합니다.

Concurrent.Thread를 사용하면 프로그램이 길고 연속성이 강한 경우에도 스레드 간 실행 환경을 자유롭게 전환할 수 있습니다. 이를 수행하는 방법에 대해 간략하게 논의할 수 있습니다. 즉, 코드 스위치가 필요합니다. 대략적으로 말하면 create()에 전달된 함수는 먼저 문자열로 변환된 다음 일괄적으로 실행될 수 있을 때까지 다시 작성됩니다. 그런 다음 이러한 프로그램은 스케줄러에 따라 단계별로 실행될 수 있습니다. 스케줄러는 여러 스레드를 조정하는 역할을 담당합니다. 즉, 수정된 모든 함수가 동일한 실행 기회를 갖도록 적절한 경우 조정할 수 있습니다. Concurrent.Thread는 실제로 새 스레드를 생성하지 않고 원래 단일 스레드를 기반으로 하는 다중 스레드 환경을 시뮬레이션합니다.

변환된 함수가 서로 다른 스레드에서 실행되는 것처럼 보이지만 실제로는 이 모든 작업을 수행하는 스레드가 하나뿐입니다. 변환된 기능 내에서 동기 통신을 수행하면 여전히 브라우저가 정지되며 이전 문제가 전혀 해결되지 않았다고 생각할 수도 있습니다. 그러나 걱정할 필요가 없습니다. Concurrent.Thread는 JavaScript의 비동기 통신 방법을 사용하여 구현된 사용자 정의 통신 라이브러리를 제공합니다. 이는 하나의 스레드가 서버의 응답을 기다리는 동안 다른 스레드가 실행될 수 있도록 설계되었습니다. 이 통신 라이브러리는 Concurrent.Thread.Http 아래에 있습니다. 사용법은 다음과 같습니다.





get() 메소드는 이름에서 알 수 있듯이 HTTP GET 메소드를 통해 얻을 수 있습니다. 대상 URL을 첫 번째 매개변수로 사용하고 HTTP 요청 헤더를 나타내는 배열을 선택적인 두 번째 매개변수로 사용하는 URL의 콘텐츠를 지정합니다. get() 메소드는 서버와 상호 작용하고 서버로부터 응답을 받은 후 XMLHttpRequest 객체를 반환 값으로 반환합니다. get() 메서드가 반환되면 서버 응답을 받은 것이므로 결과를 받기 위해 콜백 함수를 사용할 필요가 없습니다. 당연히 프로그램이 서버의 응답을 기다리는 동안 브라우저가 멈추는 것에 대해 더 이상 걱정할 필요가 없습니다. 또한 서버에 데이터를 보내는 데 사용할 수 있는 post() 메서드가 있습니다.