Web Worker 간단 정리하기
Web Worker 에 대해서 간단하게 정리 해보고자 한다.
Web Worker 를 이해하기 위해서는 먼저 Modern Browser Thread 의 특성을 이해할 필요가 있다.
https://d2.naver.com/helloworld/2922312
Javascript 는 Single Thread 로 동작한다. 하지만 브라우저는 Single Thread 로 동작하지 않는다.
브라우저에서 처리되는 Network 통신이나 I/O 들은 서로 다른 Thread 에서 동작한다.
Web Worker 는 브라우저의 Main Thread 와 별개로 작동되는 Thread 를 생성할 수 있다.
Web Worker 로 생성한, Thread 는 브라우저 렌더링 같은 Main Thread 의 작업을 방해하지 않고, 새로운 Thread 에서 스크립트를 실행하는 간단한 방법을 제공한다.
Web Worker 간단 예제
다음은 Web Worker 를 만들 수 있는 간단한 예제이다.
Web Worker 가 실행할 수 있는 별도의 Javascript 파일이 필요하다.
// worker.jsvar n = 'done';
// 오래걸리는
// 무언가를
// 처리함...
postMessage(n);
그리고, 이를 생성할 Javascript 가 필요하다.
<body><!-- some --></body>
<script>
if( window.Worker ) {
var worker = new Worker('worker.js');
worker.onmessage = (event) => {
console.log(event.data);
// 'done'
worker.terminate();
};
}
</script>
예제과정
- main thread 에서
new Worker('worker.js')
로 worker thread 를 만든다.
2. main thread 에서 worker.onmessage
에 worker 의 메시지를 전달받기위한 이벤트 핸들러를 등록한다.
3. worker thread 에서 worker.js
의 작업 처리후 postMessage()
로 데이터를 main thread 의 worker.onmessage
에게 전달한다.
4. 만약 더이상 worker thread 가 필요 없거나, 작업을 종료해야 한다면, worker.terminate()
를 호출한다.
Web Worker Scope
Web Worker 은 main thread 와 별도의 worker thread 를 가진다.
main thread 에서는 window 가 GlobalScope 이다.
worker thread 는 main thread 의 window 와는 별도의 WorkerGlobalScope 를 가진다.
때문에 Window 의 메서드나 DOM 조작이 불가능하다.
self 를 이용해서, WorkerGlobalScope 에 접근이 가능하다.
self.onmessage = () => { }
worker thread 는 Message System 을 제외하면, main thread 의 window 에 직접 접근할 수 없고, DOM 에 접근하기 위해서는 반드시 worker thread 에서 main thread 에 Message System 로 데이터를 전달해야 한다.
데이터를 주고 받기 위해서, Message System (postMessage, onmessage) 을 이용하는데, 이때 전달하는 데이터는 복사하여, 새로운 값으로 만들어 전달한다.
만약 function 을 전달하면, 복사 할 수 없기 때문에, Error 가 발생되고, class 를 전달한다고 하더라도, Method 는복사되지 않는다.
class Test {
value = true;
onAction() {}
}const test = new Test();
postMessage(test);
// { value: true }test.value = false;
postMessage(test);
// { value: false }postMessage(() => {});
// Uncaught DOMException
Web Worker Child Thread
worker thread 내에서 새로운 thread 를 생성 할 수 있다.
이때 worker thread 는 main thread 의 Origin 과 동일한데,동일한 Origin 을 가지는 javascript 를 가져올 수 있으며, 이를 가지고, 새로운 worker thread 를 만들 수 있다.
// worker.jsvar sub_worker = new Worker('sub_worker.js');
sub_worker.onmessage = function (event) {
console.log('worker', event.data);
}
sub_worker.postMessage('test');
postMessage 메서드를 이용해 부모 나 자식 Thread 에게 데이터를 전달할 수 있으며, onmessage 를 이용하여 부모나 자식 Thread 가 전달하는 데이터를 수신할 수 있다.
// sub_worker.jsonmessage = function (event) {
console.log('sub_worker', event.data);
postMessage(event.data);
}
Web Worker importScripts
window (main thread) 의 scope 에 접근할 수 없기 때문에, 만약 외부 라이브러리를 써야 한다면, importScripts 를 사용하여 다른 외부 스크립트를 가져올 수 있다.
importScripts 로 가져온 스크립트는 해당 worker thread 에만 영향을 주며 전역값 또한 worker thread 의 self (WorkerGlobalScope) 에 속하게 된다.
// test1.js
var test1 = 'test1';// test2.js
var test2 = 'test2';// worker.s
importScripts('test1.js', 'test2.js');
console.log(test1, test2);importScripts('index.html');
// Refused to execute script
만약 MIME 타입이 Javascript 가 아닐 경우 Error 가 발생한다.
Web Worker Error
Worker Thread 내에서 Error 가 발생하면, Main Thread 까지 전파되지 않는다.
필요하다면, onerror 메서드를 이용하여 ErrorEvent 를 수신할 수 있다.
// err_worker.js
onmessage = (e) => {
console.log(e.data);
}
throw new Error('error-thread');
Error 보다 onmessage 가 먼저 등록 되었기 때문에, 위 예제의 메시지 수신이 가능하다.
var err_worker = new Worker('err_worker.js');
err_worker.onerror = (err) => {
// err: ErrorEvent { ... }
console.err(err.message);
}
err_worker.onmessageerror = (err) => {
// Uncaught DataCloneError: Failed to execute
console.err(err.message);
}
err_worker.postMessage(10);
onmessageerror 는 postMessage 로 데이터 전달 중 문제가 발생하면 호출된다.
Web Worker 종류
Web Worker 는 2가지 타입이 존재한다.
- Dedicated Worker: 처음 생성 한 스크립트에서만 사용
- Shared Worker: 여러 스크립트에서 사용
new Worker 로 생성한 Worker 는 Dedicated Worker 이며, 부모 자식 간의 Thread 끼리만 메시지 교환이 가능하다.
Shared Worker 는 Worker 가 동일한 도메인 내에 존재하는 여러 Thread 에서 사용이 가능하며, Port 를 이용해 통신 한다.
Shared Worker
SharedWorker 는 다른 컨텍스트 (tab, iframe, worker) 에서 접근이 가능한 워커다.
SharedWorker 는 SharedWorkerGlobalScope 를 가지며 port 를 이용해서, 데이터를 주고 받을 수 있다.
SharedWorkerGlobalScope 은 onconnect 로 접속한 port 에 연결한다.
// shared_worker.jsvar count = 0;
onconnect = function (e) {
var port = e.ports[0];
port.onmessage = function (e) {
port.postMessage(count++);
}
}
count 는 Shared Worker 가 더이상 다른 Thread 에서 참조하지 않을때 초기화 된다.
<body><!-- some --></body>
<script>
if (window.SharedWorker) {
var worker = new SharedWorker('shared_worker.js');
worker.port.postMessage();
worker.port.onmessage = function (e) {
console.log(e.data);
}
}
위 예제는 같은 url 로 접근하는, 서로 다른 탭에서 접근할때, count 값을 공유하는 것을 볼 수 있다.