[컴][웹] HTML5 Web Worker

ref. 1> The Worker Environment > Subworkers

각 worker 에 대해서 대부분의 browser 는 seperate process 를 만든다(spawn) 고 한다. 그리고 공유되는 data 는 전부 copy 로 이뤄진다고 한다.

Web worker 사용

  1. new Worker() : new Worker(URL) 을 통해 Worker 를 생성하고,
  2. worker.onmessage : worker 에서 넘어오는 message 를 처리할 worker.onmessage 를 정의해준다.
  3. worker.onerror : worker 에서 발생하는 error 를 처리할 함수를 정의해 준다.
  4. self.onmessage : .js 등 Worker 의 source code 에서는 postMessage 로 넘어오는 data 를 처리할 self.onmessage 를 정의한다.
  5. self.postMessage : 그리고 결과값등 main page 로 전달할 사항은 self.postMessage 로 넘겨준다.
  6. worker.postMessage() : worker 의 postMessage 를 호출하므로써 worker 의 동작을 시작할 수 있다.

위에서 정의하는 on* 함수는

  • worker.addEventListener('message', func_name, false);

등의 방법으로도 정의할 수도 있다.

ref.2 에 있는 그림을 보면, 아래처럼 동작하는 것을 알 수 있다.

HTML, Worker.postMessage ----> Worker, self.onmessage Worker, self.postMessage ----> HTML, Worker.onmessage

ref. 1 > Full example 에 있는 code 를 분석해 보면, 아래와 같은 흐름을 보인다. worker.postMessage(); -> self.onmessage -> self.postMessage -> worker.onmessage

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
</head>
<body>

  <div id="log"></div>

  <script id="worker1" type="javascript/worker">
    // This script won't be parsed by JS engines
    // because its type is javascript/worker.
    self.onmessage = function(e) {
      self.postMessage('msg from worker');
    };
    // Rest of your worker code goes here.
  </script>

  <script>
    function log(msg) {
      // Use a fragment: browser will only render/reflow once.
      var fragment = document.createDocumentFragment();
      fragment.appendChild(document.createTextNode(msg));
      fragment.appendChild(document.createElement('br'));
      document.querySelector("#log").appendChild(fragment);
    }

    var blob = new Blob([document.querySelector('#worker1').textContent]);

    var worker = new Worker(window.URL.createObjectURL(blob));
    worker.onmessage = function(e) {
      log("Received: " + e.data);
    }
    worker.postMessage(); // Start the worker.
  </script>
</body>
</html>

Web Worker 개수

ref.3 의 개인적인 test 에 따르면 100개 이상에서 performance 가 느려졌다고 한다. 이 Tester 의 말로는 자신의 computer 에서는 8개가 가장 좋다고 했다.

Examples

Web Worker downloader

Web Worker 를 이용해서 XMLHttpRequest 로 download 를 하고 이것을 blob 으로 저장한 후 download 하는 예제이다.

http://localhost:8080/resources/download_test.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
</head>
<body>

  <div id="log"></div>

  <script id="worker1" type="javascript/worker">
    // This script won't be parsed by JS engines
    // because its type is javascript/worker.
    self.onmessage = function(e) {
      self.postMessage('msg from worker');
      var xhr = new XMLHttpRequest();
      xhr.open('GET', 'http://localhost:8080/resources/testflower.jpg', true);
      xhr.responseType = 'blob';

      xhr.onload = function(e) {
        if (this.status == 200) {
          // Note: .response instead of .responseText
          var blob = new Blob([this.response], {type: 'image/png'});
          self.postMessage(blob);
          
        }
      };

      xhr.send();
      self.postMessage('msg from worker2');
    };
    // Rest of your worker code goes here.
  </script>

  <script>
    function log(msg) {
      // Use a fragment: browser will only render/reflow once.
      var fragment = document.createDocumentFragment();
      fragment.appendChild(document.createTextNode(msg));
      fragment.appendChild(document.createElement('br'));

      document.querySelector("#log").appendChild(fragment);
    }

    var blob = new Blob([document.querySelector('#worker1').textContent]);

    var worker = new Worker(window.URL.createObjectURL(blob));
    worker.onmessage = function(e) {
      log("Received: " + e.data);
      console.log(e.data)
      if(typeof e.data == "object"){
        var a = document.getElementById('savelink');
        a.href = window.URL.createObjectURL(e.data);
        a.click();

      }
    }
    worker.postMessage(""); // Start the worker.
  </script>

  <a id="savelink" download="hello.png" href="data:text/plain,Hello%20world!"></a>
</body>
</html>

Multiple Web Worker

아래 예제는 여러개의 Web Worker 를 이용해서 동시에 download 를 하는 예제이다.

http://localhost:8080/resources/download_test_2.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
</head>
<body>

  <div id="log"></div>

  <script id="worker1" type="javascript/worker">
    // This script won't be parsed by JS engines
    // because its type is javascript/worker.
    self.onmessage = function(e) {
      self.postMessage('msg from worker');
      var xhr = new XMLHttpRequest();
      xhr.open('GET', 'http://localhost:8080/resources/testflower.jpg', true);
      xhr.responseType = 'blob';

      xhr.onload = function(e) {
        if (this.status == 200) {
          // Note: .response instead of .responseText
          var blob = new Blob([this.response], {type: 'image/png'});
          self.postMessage(blob);
          
        }
      };

      xhr.send();
      self.postMessage('msg from worker2');
    };
    // Rest of your worker code goes here.
  </script>

  <script>


    function log(msg) {
      // Use a fragment: browser will only render/reflow once.
      var fragment = document.createDocumentFragment();
      fragment.appendChild(document.createTextNode(msg));
      fragment.appendChild(document.createElement('br'));

      document.querySelector("#log").appendChild(fragment);
    }

    function workerOnMessage(e) {
      log("Received: " + e.data);
      console.log(e.data)
      if(typeof e.data == "object"){
        var a = document.getElementById('savelink');
        a.href = window.URL.createObjectURL(e.data);
        a.click();

      }
    }


    var blob = new Blob([document.querySelector('#worker1').textContent]);

    var worker1 = new Worker(window.URL.createObjectURL(blob));
    var worker2 = new Worker(window.URL.createObjectURL(blob));
    var worker3 = new Worker(window.URL.createObjectURL(blob));

    worker1.onmessage = workerOnMessage;
    worker2.onmessage = workerOnMessage;
    worker3.onmessage = workerOnMessage;


    worker1.postMessage(""); // Start the worker.
    worker2.postMessage(""); // Start the worker.
    worker3.postMessage(""); // Start the worker.
  </script>

  <a id="savelink" download="hello.png" href="data:text/plain,Hello%20world!"></a>
</body>
</html>

참고로, Using web workers - Web developer guide | MDN 에 따르면 IE 10 은 Blob URIs 를 인자로 사용해서 Worker 를 생성할 수 없다. SecurityError 가 발생한다. 그래서 IE 에서 테스트를 하려면 worker 에 해당하는 script 를 따로 .js 로 만들어서 new Worker() 를 하자.

Web Worker uploader

inline 으로 worker 생성

from ref. 6

function createInlineWorker(fn) {
    let blob = new Blob(['self.onmessage = ', fn.toString()], { type: 'text/javascript' });
    let url = URL.createObjectURL(blob);

    return new Worker(url);
}

let myWorker = createInlineWorker(function (e) {
    self.postMessage(e.data.toUpperCase());
});
  
myWorker.onmessage = function (e) {
    console.log(e.data); // HELLO FROM AN INLINE WORKER!
};
  
myWorker.postMessage('hello from an inline worker!')

References

  1. The Basics of Web Workers - HTML5 Rocks
  2. 박종명의 아름다운 개발 since 2010.06
  3. javascript - Number of Web Workers Limit - Stack Overflow
  4. Pro HTML5 Programming: Chapter 10 | Using the Web Workers API
  5. Blobs | O’Reilly | Javascript: The Definitive Guide | HTML5 APIs
  6. How to use Web Workers in Vue.js | Our Code World

댓글 없음:

댓글 쓰기