상세 컨텐츠

본문 제목

[JavaScript] 웹워커 Web Worker 웹에서 멀티 스레드 구현하기

WEB/JavaScript

by AlrepondTech 2017. 11. 6. 15:42

본문

반응형
728x170

 

 

 

 

=================================

=================================

=================================

 

 

 

 

 

출처: http://blog.naver.com/PostView.nhn?blogId=hihihichoi&logNo=20141585593

 

 

▶ 브라우저의 스레드

• 기본적으로 웹 페이지는 하나의 스레드로 처리

• 처리 시간이 오래 걸리는 스크립트의 실행 동안에는 UI(DOM, 사용자 이벤트 처리) 작업을 처리할 수 없음

 

▶ Worker

• 메인 스레드(UI)와 독립되어 백그라운드 프로세스로 처리되는 스크립트

• 처리 시간이 오래 걸리는 작업에 사용하면 메인 스레드와 같이 병행하여 동작

• 워커를 이용한 병렬 처리는 멀티 코어에서 최적화된 성능을 발휘

• UI 처리와 비즈니스 로직의 분리

 

▶ Worker의 종류

• 전용(dedicated) 워커

- 워커 객체와 백그라운드 프로세스가 일대일로 대응

• 공유(shared) 워커

- 다수의 워커 객체에 의해 공유되는 백그라운드 프로세스(데이터 공유)

 

▶ UI 스레드와 Worker

• 워커는 UI 스레드에서 생성되면 독립적인 프로세스로 동작

• UI 스레드가 가지고 있는 DOM 객체(window, document 등) 접근 불가

• 워커에서 처리한 결과를 화면에 반영할 때에는 MessageEvent를 이용하여 비동기 메시지 전달(UI와 워커, 워커와 워커 간 메시지 전달)

 

▶ Worker 작성

• 독립된 js 파일로 작성

• 일반 자바스크립트와 다른 환경이기 때문에 전용의 속성과 메서드 사용

• Worker 내에서 사용하는 주요 메서드

                  - importScripts(url, [url]...) : 워커에서 사용할 외부 스크립트 로딩

- postMessage(message, [ports]) : 워커를 생성한 곳으로 메시지 전송

• Worker 내에서 사용하는 주요 이벤트

             - message : 워커를 생성한 곳으로부터 메시지 수신 시 발생

 

▶ Worker 생성

• 생성자에 js 파일(워커)을 지정하여 생성

- new Worker("myworker.js");

• 워커가 생성되는 순간 바로 워커의 스크립트가 실행

 

▶ Worker 인터페이스

• 주요 메서드

- postMessage(message, [ports]) : 워커에 메시지 전송

      - terminate() : 워커를 강제로 종료

• 주요 이벤트

                        - message : 워커의 메시지 수신 시 발생

 

▶ Worker를 이용한 프로그래밍 절차

• js 파일로 워커 작성

- message 이벤트 지정(처리할 작업)

      - 처리할 작업이 끝나면 postMessage() 메서드로 결과 송신

• 메인 스레드에서 워커 생성

      - var worker = new Worker("myworker.js);

• 워커에 메시지 전달

- message 이벤트 지정(워커에서 보내는 결과 수신)

- postMessage() 메서드로 워커에 메시지 전달(작업 지시)

 

▶ 공유 워커

• 다수의 워커에게 공유되는 백그라운드 프로세스

• 생성

- new SharedWorker(worker, workerName);

• 동일한 워커를 동일한 이름으로 생성하면 워커 프로세스는 하나만 생기고 두 워커 객체는 하나의 프로세서를 공유함(브라우저의 다른 윈도우에서도 가능)

 

▶ SharedWorker 인터페이스

• 주요 속성

- port : 공유 워커와 통신할 수 있는 MessagePort 객체

• 주요 메서드

- Worker와 동일(postMessage() 메서드는 port의 메서드 사용)

• 주요 이벤트

                  - connect : SharedWorker 생성 시 발생

 

▶ 공유 워커에서 구현할 내용

• connect 이벤트 지정

- SharedWorker 생성 시 호출 되며 인자 값인 MessageEvent 객체의 ports 속성으로 클라이언트와 메시지 송수신

- 포트의 postMessage() 메서드를 이용하여 클라이언트에 메시지 송신

- 포트의 message 이벤트를 이용하여 클라이언트로부터의 메시지 수신

▶ 클라이언트에서 구현할 내용

• SharedWorker 생성

• SharedWorker 객체의 port 속성(MessagePort 타입)을 이용하여 공유 워커와 메시지 송수신

- postMessage() 메서드를 이용하여 공유 워커에 메시지 송신

- message 이벤트를 이용하여 공유 워커로부터의 메시지 수신

[출처] html5_Web Workers|작성자 두기

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://egloos.zum.com/jaures/v/2316325

 

 

JavaScript는 single thread 이다. 즉, 어떤 작업이 진행중인 상황에서 다른 작업을 동시에 실행시킬 수 없다는 의미인데 이러한 한계로 인해 복잡한 작업이 실행되어야 하는 경우에는 실행속도가 현저하게 떨어질 수밖에 없다.
 
Firefox 3.5 (이전 3.1 버전)에서는 이를 극복하기 위한 Web Workers thread가 최초로 도입 되었는데, 간단히 얘기하자면 Web Workers는 백그라운드에서 thread를 생성해 현재 페이지에서 다른 작업이 진행중이더라도 별도의 thread가 생성되어 single thread의 한계를 극복할 수 있도록 도와준다.
 
(참고 : HTML5 표준 작업에 draft recommendation으로 추가되어 있고 계속 논의가 진행중 이다.)
 
이는 multi-thread와는 좀 다른 개념인데, 다른 thread를 생성해 처리할 수 있도록 하지만 multi-thread와 같이 완전히 개별적인 thread는 아니라는 의미이다.
 
 
Web Workers에서의 제약
 
1. DOM에 접근하거나 조작할 수 없다.
2. Thread를 생성한 생성자와만 메세지를 이용한 통신을 할수 있다.
 
 
Web Workers에서 가능한 작업
 
1. XHR을 이용한 I/O 작업을 할수 있다.
2. sub-wokers를 생성할 수 있다. 즉, 생성된 thread에서 다른 thread를 생성할 수 있다.
3. timout과 interval을 사용할 수 있다.
4. Global function에 접근이 가능하다.
5. importScripts()를 이용해 다른 스크립트를 로딩할 수 있다.
 
 
WebWorkers thread를 생성하는 일은 간단하다.
 
1. 우선, thread를 생성하고 호출하는 메인 페이지에서의 작업은 아래와 같은 순서로 한다.
 
a. Web Workers Thread 생성
    var myWorker = new Worker("스크립트명.js"); 
    
b. Callback 함수 지정    
    myWorker.onmessage = function(event) {
alert("worker thread에서 호출!");
    };  
 
c. 에러 이벤트 핸들러
    myWorker.onerror = function(event) {
throw event.data;
    };
 
d. Thread에 data 전달
    myWorker.postMessage("data 값");
 
e. Thread 종료
    myWoker.terminate();
 
 
2. 그리고 Web Workers thread로 생성되어 처리되는 스크립트는 아래와 같은 순서로 한다.
 
a. 메세지를 전달받는 이벤트 핸들러
    onmessage = function(event) {
var n = parseInt(event.data);
postMessage(n+jaures);  // thread 생성자에게 메세지를 전달
   }
 
b. 에러 이벤트 핸들러
    function errorReceiver(event) {
throw event.data;
       }
       
 
위의 내용을 토대로 테스트 코드를 만들어 보면...
 
workersTest.html
 
<div id="result"></div>
 
<script type="text/javascript">
var worker = new Worker("workersTest1.js");
 
worker.onmessage = function(event) {
document.getElementById("result").textContent = "결과 : "+ event.data;
};
 
worker.onerror = function(e) {
alert("메세지: "+ e.message +"\n파일명: "+ e.filename +"\n라인번호: "+ e.lineno);
};
 
worker.postMessage("2");
</script>
 
 
workersTest1.js
 
importScripts("workersTest2.js");
 
function errorReceiver(event) {
throw event.data;
}
 
onmessage = function(event) {
var n = parseInt(event.data);
postMessage(n+jaures);
}
 
workersTest2.js
 
var jaures = 1234;
 
 
결과값은 workersTest.html 파일의 result div 에 "결과 : 1236" 이 출력된다.
 
Thread 스크립트(workersTest1.js)의 onmessage 함수에서 의도적으로 에러를 발생시키면, 생성자(workersTest.html)의 worker.onerror 에러 이벤트 핸들러를 통해 메세지, 파일명, 발생 라인번호를 출력할 수 있다.
 
또한 소스를 살펴보면 importScripts를 통해 다른 스크립트를 로딩해 worker thread 에서 값을 사용하는 것도 살펴볼 수 있다.
 
테스트 코드는 thread를 생성해 활용할 만한 가치가 있는 코드는 아니지만, web workers thread에 대해 쉽게 이해할 수 있도록 도움이 될것이다.
 
 
+ 참고 link : 

 

 

 

 

=================================

=================================

=================================

 

 

 

 

출처: https://www.sitepoint.com/javascript-threading-html5-web-workers/

 

 

What is a Web Worker?

A web worker is a single JavaScript file loaded and executed in the background. There are two types:

  • Dedicated workers: these are linked to their creator (the script which loaded the worker).
  • Shared workers: the allow any script from the same domain (somesite.com) to communicate with the worker.

Today, we’re looking at dedicated web workers…

Creating a Dedicated Web Worker

To create a dedicated web worker, you pass a JavaScript file name to a new instance of the Worker object:

 var worker = new Worker("thread1.js"); 
 

Communicating With a Dedicated Web Worker

Since the web worker cannot access the DOM or execute functions within the page’s script, all communication is handled through an event interface. The web page script passes a single data argument via the postMessage() method and receives one back via an onmessage event handler, e.g.

pagescript.js:

 var worker = new Worker("thread1.js");  // receive messages from web worker worker.onmessage = function(e) { 	alert(e.data); };  // send message to web worker worker.postMessage("Jennifer"); 

The web worker script receives and sends data via it’s own onmessage event handler and postMessage() method accordingly:

thread1.js:

 self.onmessage = function(e) { 	self.postMessage("Hello " + e.data); }; 

The message data can be a string, number, boolean, array, object, null or undefined. Data is always passed by value and serialized then de-serialized during the communication process.

Handling Dedicated Web Worker Errors

Web worker code is unlikely to be perfect, and logic errors could be caused by the data passed by the page script. Fortunately, errors can be caught using an onerror event handler. The handler event is passed an object with 3 properties:

  • filename: the name of the script which caused the error;
  • lineno: the line number where the error occurred; and
  • message: a description of the error.

pagescript.js:

 worker.onerror = function(e) { 	alert("Error in file: "+e.filename+"nline: "+e.lineno+"nDescription: "+e.message); }; 

Loading Further JavaScript Files

One or more additional JavaScript libraries can be loaded within a web worker using importScripts(), e.g.

 importScripts("lib1.js", "lib2.js", "lib3.js"); 

Alternatively, you could load further web workers … but, I’d recommend keeping it simple until browsers catch up with your threading ambitions!

Stopping a Dedicated Web Worker

The web worker thread can be stopped using the close() method, e.g.

thread1.js:

 self.onmessage = function(e) { 	if (e.data == "STOP!") self.close(); }; 

This discards any tasks awaiting processing and prevents further events being queued.

That’s all you need to know about dedicated web workers. In my next post, we’ll discuss shared web workers — a more complex beast!

 

 

 

=================================

=================================

=================================

 

 

 

 

출처: https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API/basic_usage

 

 

 

웹 워커는 웹 컨텐츠를 위해서 백그라운드 스레드에서 스크립트를 실행할 간편한 방법을 제공합니다. 워커 스레드는 사용자 인터페이스(UI)를 방해하지 않고 작업을 수행할 수 있습니다. 또한 워커는 ( responseXML과 channel속성이 언제나 null이지만) XMLHttpRequest 를 사용하여 I/O작업을 수행할 수도 있습니다. 워커는 생성이 된 후에 생성자가 명시한 이벤트 핸들러로 메세지를 올려서 자신의 하위 작업(spawning task)에 메세지를 전달할 수 도 있습니다. 본 글에서 전용 워커와 공유 워커에 대하여 소개합니다.

Web Workers APIEdit

A worker is an object created using a constructor (e.g. Worker()) that runs a named JavaScript file — this file contains the code that will run in the worker thread; workers run in another global context that is different from the current window. Thus, using the window shortcut to get the current global scope (instead of self) within a Worker will return an error.

The worker context is represented by a DedicatedWorkerGlobalScope object in the case of dedicated workers (standard workers that are utilized by a single script; shared workers use SharedWorkerGlobalScope). A dedicated worker is only accessible from the script that first spawned it, whereas shared workers can be accessed from multiple scripts.

Note: See The Web Workers API landing page for reference documentation on workers and additional guides.

You can run whatever code you like inside the worker thread, with some exceptions. For example, you can't directly manipulate the DOM from inside a worker, or use some default methods and properties of the window object. But you can use a large number of items available under window, including WebSockets, and data storage mechanisms like IndexedDB and the Firefox OS-only Data Store API. See Functions and classes available to workers for more details.

Data is sent between workers and the main thread via a system of messages — both sides send their messages using the postMessage() method, and respond to messages via the onmessage event handler (the message is contained within the Messageevent's data attribute.) The data is copied rather than shared.

Workers may, in turn, spawn new workers, as long as those workers are hosted within the same origin as the parent page. In addition, workers may use XMLHttpRequest for network I/O, with the exception that the responseXML and channel attributes on XMLHttpRequest always return null.

Dedicated workersEdit

As mentioned above, a dedicated worker is only accessible by the script that called it. In this section we'll discuss the JavaScript found in our Basic dedicated worker example (run dedicated worker): This allows you to enter two numbers to be multiplied together. The numbers are sent to a dedicated worker, multiplied together, and the result is returned to the page and displayed.

This example is rather trivial, but we decided to keep it simple while introducing you to basic worker concepts. More advanced details are covered later on in the article.

Worker feature detection

For slightly more controlled error handling and backwards compatibility, it is a good idea to wrap your worker accessing code in the following (main.js):

if (window.Worker) {    ...  }

Spawning a dedicated worker

Creating a new worker is simple. All you need to do is call the Worker() constructor, specifying the URI of a script to execute in the worker thread (main.js):

var myWorker = new Worker("worker.js");

Sending messages to and from a dedicated worker

The magic of workers happens via the postMessage() method and the onmessage event handler. When you want to send a message to the worker, you post messages to it like this (main.js):

first.onchange = function() {   myWorker.postMessage([first.value,second.value]);   console.log('Message posted to worker'); }  second.onchange = function() {   myWorker.postMessage([first.value,second.value]);   console.log('Message posted to worker'); }

So here we have two <input> elements represented by the variables first and second; when the value of either is changed, myWorker.postMessage([first.value,second.value]) is used to send the value inside both to the worker, as an array. You can send pretty much anything you like in the message.

In the worker, we can respond when the message is received by writing an event handler block like this (worker.js):

onmessage = function(e) {   console.log('Message received from main script');   var workerResult = 'Result: ' + (e.data[0] * e.data[1]);   console.log('Posting message back to main script');   postMessage(workerResult); }

The onmessage handler allows us to run some code whenever a message is received, with the message itself being available in the message event's data attribute. Here we simply multiply together the two numbers then use postMessage() again, to post the result back to the main thread.

Back in the main thread, we use onmessage again, to respond to the message sent back from the worker:

myWorker.onmessage = function(e) {   result.textContent = e.data;   console.log('Message received from worker'); }

Here we grab the message event data and set it as the textContent of the result paragraph, so the user can see the result of the calculation.

Note: The URI passed as a parameter to the Worker constructor must obey the same-origin policy .

There is currently disagreement among browsers vendors on what URIs are of the same-origin; Gecko 10.0 (Firefox 10.0 / Thunderbird 10.0 / SeaMonkey 2.7) and later do allow data URIs and Internet Explorer 10 does not allow Blob URIs as a valid script for workers.

Note: Notice that onmessage and postMessage() need to be hung off the Worker object when used in the main script thread, but not when used in the worker. This is because, inside the worker, the worker is effectively the global scope.

Note: When a message is passed between the main thread and worker, it is copied or "transferred" (moved), not shared. Read Transferring data to and from workers: further details for a much more thorough explanation.

Terminating a worker

If you need to immediately terminate a running worker from the main thread, you can do so by calling the worker's terminate method:

myWorker.terminate();

The worker thread is killed immediately without an opportunity to complete its operations or clean up after itself.

In the worker thread, workers may close themselves by calling their own close method:

close();

Handling errors

When a runtime error occurs in the worker, its onerror event handler is called. It receives an event named error which implements the ErrorEvent interface.

The event doesn't bubble and is cancelable; to prevent the default action from taking place, the worker can call the error event's preventDefault() method.

The error event has the following three fields that are of interest:

message
A human-readable error message.
filename
The name of the script file in which the error occurred.
lineno
The line number of the script file on which the error occurred.

Spawning subworkers

Workers may spawn more workers if they wish. So-called sub-workers must be hosted within the same origin as the parent page. Also, the URIs for subworkers are resolved relative to the parent worker's location rather than that of the owning page. This makes it easier for workers to keep track of where their dependencies are.

Importing scripts and libraries

Worker threads have access to a global function, importScripts(), which lets them import scripts. It accepts zero or more URIs as parameters to resources to import; all of the following examples are valid:

importScripts();                         /* imports nothing */ importScripts('foo.js');                 /* imports just "foo.js" */ importScripts('foo.js', 'bar.js');       /* imports two scripts */ importScripts('//example.com/hello.js'); /* You can import scripts from other origins */

The browser loads each listed script and executes it. Any global objects from each script may then be used by the worker. If the script can't be loaded, NETWORK_ERROR is thrown, and subsequent code will not be executed. Previously executed code (including code deferred using window.setTimeout()) will still be functional though. Function declarations after the importScripts()method are also kept, since these are always evaluated before the rest of the code.

Note: Scripts may be downloaded in any order, but will be executed in the order in which you pass the filenames into importScripts() . This is done synchronously; importScripts() does not return until all the scripts have been loaded and executed.

Shared workersEdit

A shared worker is accessible by multiple scripts — even if they are being accessed by different windows, iframes or even workers. In this section we'll discuss the JavaScript found in our Basic shared worker example (run shared worker): This is very similar to the basic dedicated worker example, except that it has two functions available handled by different script files: multiplying two numbers, or squaring a number. Both scripts use the same worker to do the actual calculation required.

Here we'll concentrate on the differences between dedicated and shared workers. Note that in this example we have two HTML pages, each with JavaScript applied that uses the same single worker file.

Note: If SharedWorker can be accessed from several browsing contexts, all those browsing contexts must share the exact same origin (same protocol, host, and port).

Note: In Firefox, shared workers cannot be shared between documents loaded in private and non-private windows (bug 1177621).

Spawning a shared worker

Spawning a new worker is pretty much the same as with a dedicated worker, but with a different constructor name (see index.html and index2.html) — each one has to spin up the worker using code like the following:

var myWorker = new SharedWorker("worker.js");

One big difference is that with a shared worker you have to communicate via a port object — an explicit port is opened that the scripts can use to communicate with the worker (this is done implicitly in the case of dedicated workers).

The port connection needs to be started either implicitly by use of the onmessage event handler or explicitly with the start()method before any messages can be posted. Although the multiply.js and worker.js files in the demo currently call the start()method, those calls are not necessary since the onmessage event handler is being used. Calling start() is only needed if the message event is wired up via the addEventListener() method.

When using the start() method to open the port connection, it needs to be called from both the parent thread and the worker thread if two-way communication is needed.

myWorker.port.start();  // called in parent thread
port.start(); // called in worker thread, assuming the port variable references a port

Sending messages to and from a shared worker

Now messages can be sent to the worker as before, but the postMessage() method has to be invoked through the port object (again, you'll see similar constructs in both multiply.js and square.js):

squareNumber.onchange = function() {   myWorker.port.postMessage([squareNumber.value,squareNumber.value]);   console.log('Message posted to worker'); }

Now, on to the worker. There is a bit more complexity here as well (worker.js):

onconnect = function(e) {   var port = e.ports[0];    port.onmessage = function(e) {     var workerResult = 'Result: ' + (e.data[0] * e.data[1]);     port.postMessage(workerResult);   } }

First, we use an onconnect handler to fire code when a connection to the port happens (i.e. when the onmessage event handler in the parent thread is setup, or when the start() method is explicitly called in the parent thread).

We use the ports attribute of this event object to grab the port and store it in a variable.

Next, we add a message handler on the port to do the calculation and return the result to the main thread. Setting up this messagehandler in the worker thread also implicitly opens the port connection back to the parent thread, so the call to port.start() is not actually needed, as noted above.

Finally, back in the main script, we deal with the message (again, you'll see similar constructs in both multiply.js and square.js):

myWorker.port.onmessage = function(e) {   result2.textContent = e.data;   console.log('Message received from worker'); }

When a message comes back through the port from the worker, we check what result type it is, then insert the calculation result inside the appropriate result paragraph.

About thread safetyEdit

The Worker interface spawns real OS-level threads, and mindful programmers may be concerned that concurrency can cause “interesting” effects in your code if you aren't careful.

However, since web workers have carefully controlled communication points with other threads, it's actually very hard to cause concurrency problems. There's no access to non-threadsafe components or the DOM. And you have to pass specific data in and out of a thread through serialized objects. So you have to work really hard to cause problems in your code.

Content security policyEdit

Workers are considered to have their own execution context, distinct from the document that created them. For this reasons they are, in general, not governed by the content security policy of the document (or parent worker) that created them. So for example, suppose a document is served with the following header:

Content-Security-Policy: script-src 'self'

Among other things, this will prevent any scripts it includes from using eval(). However, if the script constructs a worker, code running in the worker's context will be allowed to use eval().

To specify a content security policy for the worker, set a Content-Security-Policy response header for the request which delivered the worker script itself.

The exception to this is if the worker script's origin is a globally unique identifier (for example, if its URL has a scheme of data or blob). In this case, the worker does inherit the CSP of the document or worker than created it.

Transferring data to and from workers: further detailsEdit

Data passed between the main page and workers is copied, not shared. Objects are serialized as they're handed to the worker, and subsequently, de-serialized on the other end. The page and worker do not share the same instance, so the end result is that a duplicate is created on each end. Most browsers implement this feature as structured cloning.

To illustrate this, let's create for didactical purpose a function named emulateMessage(), which will simulate the behavior of a value that is cloned and not shared during the passage from a worker to the main page or vice versa:

function emulateMessage (vVal) {     return eval("(" + JSON.stringify(vVal) + ")"); }  // Tests  // test #1 var example1 = new Number(3); console.log(typeof example1); // object console.log(typeof emulateMessage(example1)); // number  // test #2 var example2 = true; console.log(typeof example2); // boolean console.log(typeof emulateMessage(example2)); // boolean  // test #3 var example3 = new String("Hello World"); console.log(typeof example3); // object console.log(typeof emulateMessage(example3)); // string  // test #4 var example4 = {     "name": "John Smith",     "age": 43 }; console.log(typeof example4); // object console.log(typeof emulateMessage(example4)); // object  // test #5 function Animal (sType, nAge) {     this.type = sType;     this.age = nAge; } var example5 = new Animal("Cat", 3); alert(example5.constructor); // Animal alert(emulateMessage(example5).constructor); // Object

A value that is cloned and not shared is called message. As you will probably know by now, messages can be sent to and from the main thread by using postMessage(), and the message event's data attribute contains data passed back from the worker.

example.html: (the main page):

var myWorker = new Worker("my_task.js");  myWorker.onmessage = function (oEvent) {   console.log("Worker said : " + oEvent.data); };  myWorker.postMessage("ali");

my_task.js (the worker):

postMessage("I\'m working before postMessage(\'ali\').");  onmessage = function (oEvent) {   postMessage("Hi " + oEvent.data); };

The structured cloning algorithm can accept JSON and a few things that JSON can't — like circular references.

Passing data examples

Example #1: Create a generic "asynchronous eval()"

The following example shows how to use a worker in order to asynchronously execute any JavaScript code allowed in a worker, through eval() within the worker:

// Syntax: asyncEval(code[, listener])  var asyncEval = (function () {   var aListeners = [], oParser = new Worker("data:text/javascript;charset=US-ASCII,onmessage%20%3D%20function%20%28oEvent%29%20%7B%0A%09postMessage%28%7B%0A%09%09%22id%22%3A%20oEvent.data.id%2C%0A%09%09%22evaluated%22%3A%20eval%28oEvent.data.code%29%0A%09%7D%29%3B%0A%7D");    oParser.onmessage = function (oEvent) {     if (aListeners[oEvent.data.id]) { aListeners[oEvent.data.id](oEvent.data.evaluated); }     delete aListeners[oEvent.data.id];   };    return function (sCode, fListener) {     aListeners.push(fListener || null);     oParser.postMessage({       "id": aListeners.length - 1,       "code": sCode     });   }; })();

The data URL is equivalent to a network request, with the following response:

onmessage = function (oEvent) {   postMessage({     "id": oEvent.data.id,     "evaluated": eval(oEvent.data.code)   }); }

Sample usage:

// asynchronous alert message... asyncEval("3 + 2", function (sMessage) {     alert("3 + 2 = " + sMessage); });  // asynchronous print message... asyncEval("\"Hello World!!!\"", function (sHTML) {     document.body.appendChild(document.createTextNode(sHTML)); });  // asynchronous void... asyncEval("(function () {\n\tvar oReq = new XMLHttpRequest();\n\toReq.open(\"get\", \"http://www.mozilla.org/\", false);\n\toReq.send(null);\n\treturn oReq.responseText;\n})()");

Example #2: Advanced passing JSON Data and creating a switching system

If you have to pass some complex data and have to call many different functions both on the main page and in the Worker, you can create a system which groups everything together.

First, we create a QueryableWorker class that takes the 

url

 of the worker, a default listener, and an error handler, and this class is gonna keep track of a list of listeners and help us communicate wirh the worker:

function QueryableWorker(url, defaultListener, onError){     var instance = this,         worker = new Worker(url),         listeners = {};      this.defaultListener = defaultListener || function(){};          if (onError) {worker.onerror = onError;}          this.postMessage = function(message){         worker.postMessage(message);     }      this.terminate = function(){         worker.terminate();     } }

Then we add the methods of adding/removing listeners:

this.addListeners = function(name, listener){     listeners[name] = listener; }  this.removeListeners = function(name){     delete listeners[name]; }

Here we let the worker handle two simple operations for illuatration: getting the difference of two numbers and making an alert after three seconds. In order to acheieve that we first implement a sendQuery method which queries if the worker actually has the corresponding methods to do what we want.

/*    This functions takes at least one argument, the method name we want to query.   Then we can pass in the arguments that the method needs.  */ this.sendQuery = function(){     if (arguments.length < 1){          throw new TypeError("QueryableWorker.sendQuery takes at least one argument");           return;     }     worker.postMessage({         "queryMethod": arguments[0],         "queryArguments": Array.prototype.slice.call(arguments, 1)     }); }

We finish QueryableWorker with the onmessage method. If the worker has the corresponding methods we queried, it should return the name of the corresponding listener and the arguments it needs, we just need to find it in listeners.:

worker.onmessage = function(event){     if (event.data instanceof Object &&         event.data.hasOwnProperty("queryMethodListener") &&         event.data.hasOwnProperty("queryMethodArguments")){         listeners[event.data.queryMethodListener].apply(instance, event.data.queryMethodArguments);     } else {         this.defaultListener.call(instance, event.data);     } }

Now onto the worker.  First we need to have the methods to handle the two simple operations:

var queryableFunctions = {     getDifference: function(a, b){         reply("printStuff", a - b);     },     waitSomeTime: function(){         setTimeout(function(){             reply("doAlert", 3, "seconds");         }, 3000);     } }  function reply(){     if (arguments.length < 1) {          throw new TypeError("reply - takes at least one argument");          return;      }      postMessage({          queryMethodListener: arguments[0],          queryMethodArguments: Array.prototype.slice.call(arguments, 1)      }); }  /* This method is called when main page calls QueryWorker's postMessage method directly*/ function defaultReply(message){     // do something }

And the onmessage method is now trivial:

onmessage = function(event){     if (event.data instanceof Object &&         event.data.hasOwnProperty("queryMethod") &&         event.data.hasOwnProperty("queryMethodArguments")){         queryableFunctions[event.data.queryMethod]             .apply(self, event.data.queryMethodArguments);     } else {         defaultReply(event.data);     } }

Here are the full implementation:

example.html (the main page):

<!doctype html>   <html>     <head>       <meta charset="UTF-8"  />       <title>MDN Example - Queryable worker</title>     <script type="text/javascript">     /*       QueryableWorker instances methods:         * sendQuery(queryable function name, argument to pass 1, argument to pass 2, etc. etc): calls a Worker's queryable function         * postMessage(string or JSON Data): see Worker.prototype.postMessage()         * terminate(): terminates the Worker         * addListener(name, function): adds a listener         * removeListener(name): removes a listener       QueryableWorker instances properties:         * defaultListener: the default listener executed only when the Worker calls the postMessage() function directly      */     function QueryableWorker(url, defaultListener, onError){       var instance = this,       worker = new Worker(url),       listeners = {};        this.defaultListener = defaultListener || function(){};         if (onError) {worker.onerror = onError;}        this.postMessage = function(message){         worker.postMessage(message);       }        this.terminate = function(){         worker.terminate();       }        this.addListeners = function(name, listener){         listeners[name] = listener;        }         this.removeListeners = function(name){          delete listeners[name];       }        worker.onmessage = function(event){         if (event.data instanceof Object &&           event.data.hasOwnProperty("queryMethodListener") &&           event.data.hasOwnProperty("queryMethodArguments")){           listeners[event.data.queryMethodListener].apply(instance, event.data.queryMethodArguments);         } else {           this.defaultListener.call(instance, event.data);         }       }     }      // your custom "queryable" worker     var myTask = new QueryableWorker("my_task.js");      // your custom "listeners"     myTask.addListener("printStuff", function (result) {       document.getElementById("firstLink").parentNode.appendChild(document.createTextNode(" The difference is " + result + "!"));     });      myTask.addListener("doAlert", function (time, unit) {       alert("Worker waited for " + time + " " + unit + " :-)");     }); </script> </head> <body>   <ul>     <li><a id="firstLink" href="javascript:myTask.sendQuery('getDifference', 5, 3);">What is the difference between 5 and 3?</a></li>     <li><a href="javascript:myTask.sendQuery('waitSomeTime');">Wait 3 seconds</a></li>     <li><a href="javascript:myTask.terminate();">terminate() the Worker</a></li>   </ul> </body> </html>

my_task.js (the worker):

var queryableFunctions = {   // example #1: get the difference between two numbers:   getDifference: function (nMinuend, nSubtrahend) {       reply("printSomething", nMinuend - nSubtrahend);   },   // example #2: wait three seconds   waitSomeTime: function () {       setTimeout(function() { reply("doAlert", 3, "seconds"); }, 3000);   } };  // system functions  function defaultReply (message) {   // your default PUBLIC function executed only when main page calls the queryableWorker.postMessage() method directly   // do something }  function reply () {   if (arguments.length < 1) { throw new TypeError("reply - not enough arguments"); return; }   postMessage({ "queryMethodListener": arguments[0], "queryMethodArguments": Array.prototype.slice.call(arguments, 1) }); }  onmessage = function (oEvent) {   if (oEvent.data instanceof Object && oEvent.data.hasOwnProperty("queryMethod") && oEvent.data.hasOwnProperty("queryMethodArguments")) {     queryableFunctions[oEvent.data.queryMethod].apply(self, oEvent.data.queryMethodArguments);   } else {     defaultReply(oEvent.data);   } };

It is possible to switch the content of each mainpage -> worker and worker -> mainpage message. And the property names "queryMethod", "queryMethodListeners", "queryMethodArguments" can be anything as long as they are consistent in QueryableWorker and the worker.

Passing data by transferring ownership (transferable objects)

Google Chrome 17+ and Firefox 18+ contain an additional way to pass certain types of objects (transferable objects, that is objects implementing the Transferable interface) to or from a worker with high performance. Transferable objects are transferred from one context to another with a zero-copy operation, which results in a vast performance improvement when sending large data sets. Think of it as pass-by-reference if you're from the C/C++ world. However, unlike pass-by-reference, the 'version' from the calling context is no longer available once transferred. Its ownership is transferred to the new context. For example, when transferring an ArrayBuffer from your main app to a worker script, the original ArrayBuffer is cleared and no longer usable. Its content is (quite literally) transferred to the worker context.

// Create a 32MB "file" and fill it. var uInt8Array = new Uint8Array(1024*1024*32); // 32MB for (var i = 0; i < uInt8Array.length; ++i) {   uInt8Array[i] = i; }  worker.postMessage(uInt8Array.buffer, [uInt8Array.buffer]);

Note: For more information on transferable objects, performance, and feature-detection for this method, read Transferable Objects: Lightning Fast! on HTML5 Rocks.

Embedded workersEdit

There is not an "official" way to embed the code of a worker within a web page, like <script> elements do for normal scripts. But a <script> element that does not have a src attribute and has a type attribute that does not identify an executable MIME type can be considered a data block element that JavaScript could use. "Data blocks" is a more general feature of HTML5 that can carry almost any textual data. So, a worker could be embedded in this way:

<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>MDN Example - Embedded worker</title> <script type="text/js-worker">   // This script WON'T be parsed by JS engines because its MIME type is text/js-worker.   var myVar = "Hello World!";   // Rest of your worker code goes here. </script> <script type="text/javascript">   // This script WILL be parsed by JS engines because its MIME type is text/javascript.   function pageLog (sMsg) {     // Use a fragment: browser will only render/reflow once.     var oFragm = document.createDocumentFragment();     oFragm.appendChild(document.createTextNode(sMsg));     oFragm.appendChild(document.createElement("br"));     document.querySelector("#logDisplay").appendChild(oFragm);   } </script> <script type="text/js-worker">   // This script WON'T be parsed by JS engines because its MIME type is text/js-worker.   onmessage = function (oEvent) {     postMessage(myVar);   };   // Rest of your worker code goes here. </script> <script type="text/javascript">   // This script WILL be parsed by JS engines because its MIME type is text/javascript.    // In the past...:   // blob builder existed   // ...but now we use Blob...:   var blob = new Blob(Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) { return oScript.textContent; }),{type: "text/javascript"});    // Creating a new document.worker property containing all our "text/js-worker" scripts.   document.worker = new Worker(window.URL.createObjectURL(blob));    document.worker.onmessage = function (oEvent) {     pageLog("Received: " + oEvent.data);   };    // Start the worker.   window.onload = function() { document.worker.postMessage(""); }; </script> </head> <body><div id="logDisplay"></div></body> </html>

The embedded worker is now nested into a new custom document.worker property.

It is also worth noting that you can also convert a function into a Blob, then generate an object URL from that blob. For example:

function fn2workerURL(fn) {   var blob = new Blob(['('+fn.toString()+')()'], {type: 'application/javascript'})   return URL.createObjectURL(blob) }

Further examplesEdit

This section provides further examples of how to use web workers.

Performing computations in the background

Workers are mainly useful for allowing your code to perform processor-intensive calculations without blocking the user interface thread. In this example, a worker is used to calculate Fibonacci numbers.

The JavaScript code

The following JavaScript code is stored in the "fibonacci.js" file referenced by the HTML in the next section.

var results = [];  function resultReceiver(event) {   results.push(parseInt(event.data));   if (results.length == 2) {     postMessage(results[0] + results[1]);   } }  function errorReceiver(event) {   throw event.data; }  onmessage = function(event) {   var n = parseInt(event.data);    if (n == 0 || n == 1) {     postMessage(n);     return;   }    for (var i = 1; i <= 2; i++) {     var worker = new Worker("fibonacci.js");     worker.onmessage = resultReceiver;     worker.onerror = errorReceiver;     worker.postMessage(n - i);   }  };

The worker sets the property onmessage to a function which will receive messages sent when the worker object's postMessage() is called (note that this differs from defining a global variable of that name, or defining a function with that name. var onmessage and function onmessage will define global properties with those names, but they will not register the function to receive messages sent by the web page that created the worker). This starts the recursion, spawning new copies of itself to handle each iteration of the calculation.

The HTML code

<!DOCTYPE html> <html>   <head>     <meta charset="UTF-8"  />     <title>Test threads fibonacci</title>   </head>   <body>    <div id="result"></div>    <script language="javascript">      var worker = new Worker("fibonacci.js");      worker.onmessage = function(event) {       document.getElementById("result").textContent = event.data;       dump("Got: " + event.data + "\n");     };      worker.onerror = function(error) {       dump("Worker error: " + error.message + "\n");       throw error;     };      worker.postMessage("5");    </script>   </body> </html>

The web page creates a div element with the ID result , which gets used to display the result, then spawns the worker. After spawning the worker, the onmessage handler is configured to display the results by setting the contents of the div element, and the onerror handler is set to dump the error message.

Finally, a message is sent to the worker to start it.

Try this example.

Performing web I/O in the background

You can find an example of this in the article Using workers in extensions .

Dividing tasks among multiple workers

As multi-core computers become increasingly common, it's often useful to divide computationally complex tasks among multiple workers, which may then perform those tasks on multiple-processor cores.

Other types of workerEdit

In addition to dedicated and shared web workers, there are other types of worker available:

  • ServiceWorkers essentially act as proxy servers that sit between web applications, and the browser and network (when available). They are intended to (amongst other things) enable the creation of effective offline experiences, intercepting network requests and taking appropriate action based on whether the network is available and updated assets reside on the server. They will also allow access to push notifications and background sync APIs.
  • Chrome Workers are a Firefox-only type of worker that you can use if you are developing add-ons and want to use workers in extensions and have access to js-ctypes in your worker. See ChromeWorker for more details.
  • Audio Workers provide the ability for direct scripted audio processing to be done in a web worker context.

Functions and interfaces available in workersEdit

You can use most standard JavaScript features inside a web worker, including:

The main thing you can't do in a Worker is directly affect the parent page. This includes manipulating the DOM and using that page's objects. You have to do it indirectly, by sending a message back to the main script via DedicatedWorkerGlobalScope.postMessage, then actioning the changes from there.

 

 

 

 

=================================

=================================

=================================

 

 

출처: https://www.html5rocks.com/en/tutorials/workers/basics/

The Problem: JavaScript Concurrency

There are a number of bottlenecks preventing interesting applications from being ported (say, from server-heavy implementations) to client-side JavaScript. Some of these include browser compatibility, static typing, accessibility, and performance. Fortunately, the latter is quickly becoming a thing of the past as browser vendors rapidly improve the speed of their JavaScript engines.

One thing that's remained a hindrance for JavaScript is actually the language itself. JavaScript is a single-threaded environment, meaning multiple scripts cannot run at the same time. As an example, imagine a site that needs to handle UI events, query and process large amounts of API data, and manipulate the DOM. Pretty common, right? Unfortunately all of that can't be simultaneous due to limitations in browsers' JavaScript runtime. Script execution happens within a single thread.

Developers mimic 'concurrency' by using techniques like setTimeout()setInterval()XMLHttpRequest, and event handlers. Yes, all of these features run asynchronously, but non-blocking doesn't necessarily mean concurrency. Asynchronous events are processed after the current executing script has yielded. The good news is that HTML5 gives us something better than these hacks!

Introducing Web Workers: Bring Threading to JavaScript

The Web Workers specification defines an API for spawning background scripts in your web application. Web Workers allow you to do things like fire up long-running scripts to handle computationally intensive tasks, but without blocking the UI or other scripts to handle user interactions. They're going to help put and end to that nasty 'unresponsive script' dialog that we've all come to love:

 

 

 


Common unresponsive script dialog.

Workers utilize thread-like message passing to achieve parallelism. They're perfect for keeping your UI refresh, performant, and responsive for users.

Types of Web Workers

It's worth noting that the specification discusses two kinds of Web Workers, Dedicated Workers and Shared Workers. This article will only cover dedicated workers and I'll refer to them as 'web workers' or 'workers' throughout.

Getting Started

Web Workers run in an isolated thread. As a result, the code that they execute needs to be contained in a separate file. But before we do that, the first thing to do is create a new Worker object in your main page. The constructor takes the name of the worker script:

var worker = new Worker('task.js');

If the specified file exists, the browser will spawn a new worker thread, which is downloaded asynchronously. The worker will not begin until the file has completely downloaded and executed. If the path to your worker returns an 404, the worker will fail silently.

After creating the worker, start it by calling the postMessage() method:

worker.postMessage(); // Start the worker.

Communicating with a Worker via Message Passing

Communication between a work and its parent page is done using an event model and the postMessage() method. Depending on your browser/version, postMessage() can accept either a string or JSON object as its single argument. The latest versions of the modern browsers support passing a JSON object.

Below is a example of using a string to pass 'Hello World' to a worker in doWork.js. The worker simply returns the message that is passed to it.

Main script:

var worker = new Worker('doWork.js');  worker.addEventListener('message', function(e) {   console.log('Worker said: ', e.data); }, false);  worker.postMessage('Hello World'); // Send data to our worker.

doWork.js (the worker):

self.addEventListener('message', function(e) {   self.postMessage(e.data); }, false);

When postMessage() is called from the main page, our worker handles that message by defining an onmessage handler for the message event. The message payload (in this case 'Hello World') is accessible in Event.data. Although this particular example isn't very exciting, it demonstrates that postMessage() is also your means for passing data back to the main thread. Convenient!

Messages passed between the main page and workers are copied, not shared. For example, in the next example the 'msg' property of the JSON message is accessible in both locations. It appears that the object is being passed directly to the worker even though it's running in a separate, dedicated space. In actuality, what is happening is that the object is being serialized as it's handed to the worker, and subsequently, de-serialized on the other end. The page and worker do not share the same instance, so the end result is that a duplicate is created on each pass. Most browsers implement this feature by automatically JSON encoding/decoding the value on either end.

The following is a more complex example that passes messages using JSON objects.

Main script:

<button onclick="sayHI()">Say HI</button> <button onclick="unknownCmd()">Send unknown command</button> <button onclick="stop()">Stop worker</button> <output id="result"></output>  <script>   function sayHI() {     worker.postMessage({'cmd': 'start', 'msg': 'Hi'});   }    function stop() {     // worker.terminate() from this script would also stop the worker.     worker.postMessage({'cmd': 'stop', 'msg': 'Bye'});   }    function unknownCmd() {     worker.postMessage({'cmd': 'foobard', 'msg': '???'});   }    var worker = new Worker('doWork2.js');    worker.addEventListener('message', function(e) {     document.getElementById('result').textContent = e.data;   }, false); </script>

doWork2.js:

self.addEventListener('message', function(e) {   var data = e.data;   switch (data.cmd) {     case 'start':       self.postMessage('WORKER STARTED: ' + data.msg);       break;     case 'stop':       self.postMessage('WORKER STOPPED: ' + data.msg +                        '. (buttons will no longer work)');       self.close(); // Terminates the worker.       break;     default:       self.postMessage('Unknown command: ' + data.msg);   }; }, false);

Note: There are two ways to stop a worker: by calling worker.terminate() from the main page or by calling self.close() inside of the worker itself.

Example: Run this worker!

  

Transferrable objects

Most browsers implement the structured cloning algorithm, which allows you to pass more complex types in/out of Workers such as FileBlobArrayBuffer, and JSON objects. However, when passing these types of data usingpostMessage(), a copy is still made. Therefore, if you're passing a large 50MB file (for example), there's a noticeable overhead in getting that file between the worker and the main thread.

Structured cloning is great, but a copy can take hundreds of milliseconds. To combat the perf hit, you can use Transferable Objects.

With Transferable Objects, data is transferred from one context to another. It is zero-copy, which vastly improves the performance of sending data to a Worker. Think of it as pass-by-reference if you're from the C/C++ world. However, unlike pass-by-reference, the 'version' from the calling context is no longer available once transferred to the new context. For example, when transferring an ArrayBuffer from your main app to Worker, the original ArrayBuffer is cleared and no longer usable. Its contents are (quiet literally) transferred to the Worker context.

To use transferrable objects, use a slightly different signature of postMessage():

worker.postMessage(arrayBuffer, [arrayBuffer]); window.postMessage(arrayBuffer, targetOrigin, [arrayBuffer]);

The worker case, the first argument is the data and the second is the list of items that should be transferred. The first argument doesn't have to be an ArrayBufferby the way. For example, it can be a JSON object:

worker.postMessage({data: int8View, moreData: anotherBuffer},                    [int8View.buffer, anotherBuffer]);

The important point being: the second argument must be an array of ArrayBuffers. This is your list of transferrable items.

To see the speed improvement of transferrables, check out this DEMO. For more information on transferrables, see our HTML5Rock post.

The Worker Environment

Worker Scope

In the context of a worker, both self and this reference the global scope for the worker. Thus, the previous example could also be written as:

addEventListener('message', function(e) {   var data = e.data;   switch (data.cmd) {     case 'start':       postMessage('WORKER STARTED: ' + data.msg);       break;     case 'stop':   ... }, false);

Alternatively, you could set the onmessage event handler directly (though addEventListener is always encouraged by JavaScript ninjas).

onmessage = function(e) {   var data = e.data;   ... };

Features Available to Workers

Due to their multi-threaded behavior, web workers only has access to a subset of JavaScript's features:

  • The navigator object
  • The location object (read-only)
  • XMLHttpRequest
  • setTimeout()/clearTimeout() and setInterval()/clearInterval()
  • The Application Cache
  • Importing external scripts using the importScripts() method
  • Spawning other web workers

Workers do NOT have access to:

  • The DOM (it's not thread-safe)
  • The window object
  • The document object
  • The parent object

Loading External Scripts

You can load external script files or libraries into a worker with the importScripts() function. The method takes zero or more strings representing the filenames for the resources to import.

This example loads script1.js and script2.js into the worker:

worker.js:

importScripts('script1.js'); importScripts('script2.js');

Which can also be written as a single import statement:

importScripts('script1.js', 'script2.js');

Subworkers

Workers have the ability to spawn child workers. This is great for further breaking up large tasks at runtime. However, subworkers come with a few caveats:

  • Subworkers must be hosted within the same origin as the parent page.
  • URIs within subworkers are resolved relative to their parent worker's location (as opposed to the main page).

Keep in mind most browsers spawn separate processes for each worker. Before you go spawning a worker farm, be cautious about hogging too many of the user's system resources. One reason for this is that messages passed between main pages and workers are copied, not shared. See Communicating with a Worker via Message Passing.

For an sample of how to spawn a subworker, see the example in the specification.

Inline Workers

What if you want to create your worker script on the fly, or create a self-contained page without having to create separate worker files? With Blob(), you can "inline" your worker in the same HTML file as your main logic by creating a URL handle to the worker code as a string:

var blob = new Blob([     "onmessage = function(e) { postMessage('msg from worker'); }"]);  // Obtain a blob URL reference to our worker 'file'. var blobURL = window.URL.createObjectURL(blob);  var worker = new Worker(blobURL); worker.onmessage = function(e) {   // e.data == 'msg from worker' }; worker.postMessage(); // Start the worker.

Blob URLs

The magic comes with the call to window.URL.createObjectURL(). This method creates a simple URL string which can be used to reference data stored in a DOM File or Blob object. For example:

blob:http://localhost/c745ef73-ece9-46da-8f66-ebes574789b1

Blob URLs are unique and last for the lifetime of your application (e.g. until the document is unloaded). If you're creating many Blob URLs, it's a good idea to release references that are no longer needed. You can explicitly release a Blob URLs by passing it to window.URL.revokeObjectURL():

window.URL.revokeObjectURL(blobURL);

In Chrome, there's a nice page to view all of the created blob URLs: chrome://blob-internals/.

Full Example

Taking this one step further, we can get clever with how the worker's JS code is inlined in our page. This technique uses a <script> tag to define the worker:

<!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>

In my opinion, this new approach is a bit cleaner and more legible. It defines a script tag with 

id="worker1"

 and 

type='javascript/worker'

 (so the browser doesn't parse the JS). That code is extracted as a string using document.querySelector('#worker1').textContent and passed to Blob() to create the file.

Loading External Scripts

When using these techniques to inline your worker code, importScripts() will only work if you supply an absolute URI. If you attempt to pass a relative URI, the browser will complain with a security error. The reason being: the worker (now created from a blob URL) will be resolved with a blob: prefix, while your app will be running from a different (presumably http://) scheme. Hence, the failure will be due to cross origin restrictions.

One way to utilize importScripts() in an inline worker is to "inject" the current url of your main script is running from by passing it to the inline worker and constructing the absolute URL manually. This will insure the external script is imported from the same origin. Assuming your main app is running from http://example.com/index.html:

... <script id="worker2" type="javascript/worker"> self.onmessage = function(e) {   var data = e.data;    if (data.url) {     var url = data.url.href;     var index = url.indexOf('index.html');     if (index != -1) {       url = url.substring(0, index);     }     importScripts(url + 'engine.js');   }   ... }; </script> <script>   var worker = new Worker(window.URL.createObjectURL(bb.getBlob()));   worker.postMessage({url: document.location}); </script>

Handling Errors

As with any JavaScript logic, you'll want to handle any errors that are thrown in your web workers. If an error occurs while a worker is executing, the an ErrorEvent is fired. The interface contains three useful properties for figuring out what went wrong: filename - the name of the worker script that caused the error, lineno - the line number where the error occurred, and message - a meaningful description of the error. Here is an example of setting up an onerror event handler to print the properties of the error:

<output id="error" style="color: red;"></output> <output id="result"></output>  <script>   function onError(e) {     document.getElementById('error').textContent = [       'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message     ].join('');   }    function onMsg(e) {     document.getElementById('result').textContent = e.data;   }    var worker = new Worker('workerWithError.js');   worker.addEventListener('message', onMsg, false);   worker.addEventListener('error', onError, false);   worker.postMessage(); // Start worker without a message. </script>

Example: workerWithError.js tries to perform 1/x, where x is undefined.

workerWithError.js:

self.addEventListener('message', function(e) {   postMessage(1/x); // Intentional error. };

A Word on Security

Restrictions with Local Access

Due to Google Chrome's security restrictions, workers will not run locally (e.g. from file://) in the latest versions of the browser. Instead, they fail silently! To run your app from the file:// scheme, run Chrome with the --allow-file-access-from-files flag set. NOTE: It is not recommended to run your primary browser with this flag set. It should only be used for testing purposes and not regular browsing.

Other browsers do not impose the same restriction.

Same Origin Considerations

Worker scripts must be external files with the same scheme as their calling page. Thus, you cannot load a script from a data: URL or javascript: URL, and an https: page cannot start worker scripts that begin with http: URLs.

Use Cases

So what kind app would utilize web workers? Unfortunately, web workers are still relatively new and the majority of samples/tutorials out there involve computing prime numbers. Although that isn't very interesting, it's useful for understanding the concepts of web workers. Here are a few more ideas to get your brain churning:

  • Prefetching and/or caching data for later use
  • Code syntax highlighting or other real-time text formatting
  • Spell checker
  • Analyzing video or audio data
  • Background I/O or polling of webservices
  • Processing large arrays or humungous JSON responses
  • Image filtering in <canvas>
  • Updating many rows of a local web database

Demos

References

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://html5dev.tistory.com/184

 

 

WebWorker란, HTML5 API중 하나로 JavaScript를 병렬 처리하기 위해서 표준으로 준비된 규격입니다. 일반적으로 JavaScript는 CPU의 한개 쓰레드로 연산을 하면, 시간이 걸리는 연산이라고 해도 해당 연산이 끝날 때까지 다음 명령을 받아들이지 않습니다. 반대로 WebWorker를 이용하면, 미리 처리를 정의해둔 Worker라는 임의의 타이밍을 호출하여 무거운 연산의 멀티쓰레드 처리를 가능하게 합니다. 즉, 최근 멀티코어 기기에서 JavaScript를 이용한 연산을 효율적으로 할 수 있게 되는 것입니다.

 

WebWorker 사용법

멀티쓰레드 처리라고 말하면 어럽게 느껴질 수 있지만, WebWorker를 사용하는 방법은 상당히 간단합니다. 라이브러리를 별도로 읽을 필요도 없이 다음 4가지 절차 순서대로 진행하면 됩니다.

1. Worker클래스 객체 선언

2. Worker 변수 보내기

3. Worker 실행하는 처리 정의

4. Worker로부터 처리결과 받음

 

1. Worker 클래스 객체 선언

 

 var worker = new Worker("Worker로 실행하는 처리 기술하는 파일명");

 

이렇게만 선언하면 되는데 Worker로 실행하는 처리를 기술하는 파일명을 "worker_test.js"라고 합니다. 물론 객체명은 "worker"는 임의대로 선언합니다.

 

2. Worker 변수 보내기

 

 worker.postMessage(변수명); //Worker에 변수 넘김

 

"worker"는 1번에서 선언한 Worker클래스 객체명으로 "postMessage"는 Worker클래스 객체 메소드입니다. "postMessage"에 인수를 설정하는 것으로 외부에서 실행하는 처리에 파라미터를 보낼 수 있습니다. 인수에는 보통 변수뿐만 아니라, 배열이나 객체를 줄 수 있습니다. 객체를 인수로서 사용하는 예제는 다음과 같습니다.

 

 

 var AA = {n:1, l:2, m:3}; //Worker에 넘기는 객체 선언
 worker.postMessage(AA);   //Worker에 객체 넘김

 

 

3. Worker실행하는 처리 정의

1번에서 지정한 파일에 2로 넘긴 파라미터를 이용하여 Worker로 실행하는 처리를 만듭니다.

 worker_test.js

 

onmessage = function(event) {    
   var AA = event.data;      
   // 여기에 처리를 기술함   
   var results = AA;    
   postMessage(results);

 

"event.data"에 2번 "postMessage" 인수로 설정한 변수가 있습니다. 또한 "postMessage"함수를 이용하여 처리결과를 호출하는 객체에 리턴합니다. 위 예는 동작 확인을 목적으로 하고 있으므로 받은 변수를 아무것도 처리를 하지 않고 그대로 리턴하고 있습니다.

 

4. Worker로부터 처리결과 받음

3번 "postMessage"함수 인수로 지정한 변수를 원래 프로그램으로 받습니다. 변수가 리턴되어 오면, Worker클래스 객체 메소드 "onmessage"가 호출됩니다.

 

 worker.onmessage = function(event) {    
   var BB = event.data;
}

 

"event.data"에는 3번 "postMessage" 인수로 설정한 변수가 포함되어 있습니다. 받은 처리후 데이터를 나머지를 처리하게 됩니다.

 

예제 프로그램 소스코드

메인소스코드

 

 var worker = new Worker("worker_test.js");
worker.onmessage = function(event) { //Worker로부터 받음    
   var BB = event.data;    
   document.getElementById("output").innerHTML = "n = " + BB.n + "<br>l = " + BB.l + "<br>m = " + BB.m;
}
function pushButton () {  //버튼을 클릭했을 때 처리    
   var AA = {n:1, l:2, m:3};    
   worker.postMessage(AA);      // Worker에 값을 넘김
};

 

worker_test.js

 

 onmessage = function(event) {    
   var AA = event.data; // event.data    
   // 여기에 처리를 작성    
   var results = AA;    
   postMessage(results);
}

 

 

 



출처: http://html5dev.tistory.com/184 [HTML5 Developer]

 

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://deviant86.tistory.com/483

 

 

웹워커(Web Worker)
 
사용자가 웹페이지를 이용하는 동안에 백그라운드에서 오래 걸리는 복잡한 자바스크립트 처리나 계산을 할 수 있다.
 
브라우저 Web Worker 지원 확인
if(window.Worker){
alert("이 브라우저는 웹 워커를 지원합니다")
}else{
alert("이 브라우저는 웹 워커를 지원하지 않습니다")
 
웹 워커는 HTML 페이지에서 Worker 라는 객체를 통해 실행된다
Worker 객체를 생성 시 워커의 작업을 정의한 자바스크립트파일(.js)을 전달해 줘야 한다
ex) var worker = new Worker("worker.js")  
 
Web Worker methods
postMessage - 양쪽 모두 상대에게 데이터를 송신할 때는 
postMessage()의 인자는 어떤 type이든 상관 없으며 이 인자는 json으로 직렬화 처리된다
onmessage - 데이터를 수신
terminate - 워커의 작업을 중지
 
Web Worker에서 사용 가능한 것
- Object, Array, Date, Math, String 등의 Javascript객체
- navigator object
- locatioin object(read only)
- setTimeout()/clearTimeout()/setInterval()/clearInternal()
- XMLHttpRequest
 
※ 워커를 위한 스크립트는 <body>태그 하단에 들어가게 할 것을 권장한다.

ex) worker.js
onmessage = function(evt){ //메시지 수신
for(var i=evt.data; i<1000000; i++){
postMessage(i); //1씩 증가한 값을 호출한 곳으로 전달
}
}; 
 
<!DOCTYPE HTML>
<HTML>
 <HEAD>
 <meta charset="utf-8">
  <TITLE>Web Worker example</TITLE>
 </HEAD>
 <BODY>
  <p>
  <input id="start" type="button" value="start work" onclick="startWork();"/>
  <input id="stop" type="button" value="stop working" onclick="stopWork();"/>
  </p>
  <p id="process">before</p>
  <textarea rows="4" cols="60">write...</textarea>
 </BODY>
 <script>
if(window.Worker){
alert("이 브라우저는 웹 워커를 지원합니다")
}else{
alert("이 브라우저는 웹 워커를 지원하지 않습니다")
}
 
var worker;
function makeWorker(){ //worker create
worker = new Worker("./worker.js");
}
function startWork(){
makeWorker(); //call function
worker.postMessage(0); //send value '0' to worker
worker.onmessage = function(evt){ //receive result message from worker
                        //print message 
document.getElementById("process").innerHTML = evt.data;
};
}
function stopWork(){
if(worker){
worker.terminate(); //stop working
alert("worker stopped");
}
}
worker.onerror = function(evt){
document.getElementById("process").innerHTML = "error occurred!";
}
 </script>
</HTML> 
 
=> 위 예제는 firefox에서만 제대로 동작했음.
 



출처: http://deviant86.tistory.com/483 [Cocooning_]

 

 

 

=================================

=================================

=================================

 

 


출처: http://yoonnamee.tistory.com/132 [yoon's]

 

[ch12-webworker]

01.html

 

<script type="text/javascript" src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script type="text/javascript">
    //worker객체 생성
                           //백그라운드 실행 코드가있는 js파일명 등록
    var worker = new Worker('worker1.js');
    
    $(function(){
     $('#btnOk').click(function(){
      //postMessage: 백그라운드 작업을 수행하는 객체에 데이터 전달
      worker.postMessage($('#guguNum').val());
     });
     
     //onmessage: 백그라운드에서 데이터 작업을 완료한 후 postMessage메서드에 데이터를 전달하면 이벤트 가 발생
     worker.onmessage = function(event){
      $('#result').html(event.data);//백그라운드 실행 결과값을 반환
     };
     
     worker.onerror = function(event){
      $('#result').html('에러 발생!');
     }
     $('#btnStop').click(function(){
      if(worker)
       worker.terminate();
     });
    });

</script>
</head>
<body>
<!-- 웹워커: 자바스크립트 코드를 백그라운드에서 실행시키는 기술 -->
<input type="text" id="guguNum">
<input type="button" id="btnOk" value="확인">
<input type="button" id="btnStop" value="중지">
<br>
<div id="result"></div>
</body>

 

worker1.js (new > 자바스크립트 소스파일)

 

//포그라운드에서 데이터가 도착
onmessage= function(event) {
 //백그라운드로 처리될 코드작성
 var result = "";
 var dan = event.data;//인자로 넘어온 데이터 호출
 
 for(var i=0;i<100000;i++){
  result += dan + '*' + i + '=' + dan*i + '<br>';
  
 }
 //ui쪽에 데이터 전달
 postMessage(result);
};

 



출처: http://yoonnamee.tistory.com/132 [yoon's]

 

 

 

=================================

=================================

=================================

 

 

 

출처: http://imaitman.tistory.com/232

 

'웹워커'란 JavaScript 코드를 백그라운드에서 실행시키기 위한 기술입니다.

처리 시간이 오래 걸리는 스크립트를 실행하면 브라우져가 멈춘것 처럼 보이거나 브라우져에서

스크립트 실행여부를 묻는 창이 뜨기도 하는데 이런 로직을 '웹워커'로 구현하면

UI 스레드와는 별개로 백그라운드에서 실행 시킬 수 있습니다.

하지만 '웹워커'는 워커를 생성시키는 메인 스레드와는 별개로 구동되기 때문에 window나

document객체에는 접근할 수 없습니다. 다시 말해 DOM에 접근할 수 없다는 의미가 됩니다.

 

* 워크 생성

var worker = new Worker('worker.js');

 

* 워커쪽으로 메세지 보내기

worker.postMessage('Hello');

 

* 워커에서 메세지 받기

:워커쪽에서는 onmessage이벤트 핸들러를 지정합니다.

// 워커쪽 처리

onmessage = function(e) {

    // 메세지 처리 수행

}

 

* 워커를 생성한 곳으로 메세지 보내기

// 워커쪽 처리

onmessage = function(e) {

    postMessage('Echo from worker : ' + e.data);

}

 
* 워커가 보낸 메세지 수신

worker.onmessage = function(e) {

    alert(e.data);

}

 

===============================================

<!DOCTYPE html>

<html>

<body>

 

Number : <input type='text' id='num'>

<button onclick='calculate()'>Calculate</button>

<button onclick='stop()'>Stop</button>

 

<script>

var w;

 

if(typeof(Worker)!=="undefined") {

alert("This browser supports the Web worker.");

} else {

alert("This browser doesn't support the Web worker.");

}

 

function calculate() {

// 실행 중인 워커가 있으면 정지

if (w) {

w.terminate();

}

 

var num = document.getElementById('num').value;

w = new Worker('worker.js');

 

// 워커로부터 메세지 수신 이벤트 핸들러

w.onmessage = function(event) {

alert('Sum is ' + event.data);

};

 

// 워커로 메세지 송신

w.postMessage(num);

}

 

function stop() {

// 실행 중인 워커가 있으면 정지

if (w) {

w.terminate();

}

alert('Stopped.');

 

}

 

</script>

 

</body>

</html>

 

===============================================
// work.js 파일 소스
// importScripts를 이용해서 다른 js파일도 로딩이 가능하다.
// ex) importScripts("script2.js");
 
onmessage = function(event) {
var num = event.data;
var result = 0;
 
for (var i=1;i<=num;i++) {
result += i;
}
 
postMessage(result);
};

 



출처: http://imaitman.tistory.com/232 [To be rich..]

 

 

 

 

=================================

=================================

=================================

 

 

 

출처: https://johnresig.com/blog/web-workers/

 

 

Computing with JavaScript Web Workers

Web Workers are, undoubtedly, the coolest new feature to arrive in the latest version of web browsers. Web Workers allow you to run JavaScript in parallel on a web page, without blocking the user interface.

Normally in order to achieve any sort of computation using JavaScript you would need to break your jobs up into tiny chunks and split their execution apart using timers. This is both slow and unimpressive (since you can’t actually run anything in parallel – more information on this in How JavaScript Timers Work).

With our current situation in mind, let’s dig in to Web Workers.

Web Workers

The Web Worker recommendation is partially based off of the prior work done by the Gears team on their WorkerPool Module. The idea has since grown and been tweaked to become a full recommendation.

A ‘worker’ is a script that will be loaded and executed in the background. Web Workers provide a way to do this seamlessly, for example:

new Worker("worker.js");

The above will load the script, located at ‘worker.js’, and execute it in the background.

There are some HUGE stipulations, though:

  1. Workers don’t have access to the DOM. No documentgetElementById, etc. (The notable exceptions are setTimeoutsetInterval, and XMLHttpRequest.)
  2. Workers don’t have direct access to the ‘parent’ page.

With these points in mind the big question should be: How do you actually use a worker and what is it useful for?

You use a worker by communicating with it using messages. All browsers support passing in a string message (Firefox 3.5 also supports passing in JSON-compatible objects). This message will be communicated to the worker (the worker can also communicate messages back to the parent page). This is the extent to which communication can occur.

The message passing is done using the postMessage API, working like this:

var worker = new Worker("worker.js");
 
// Watch for messages from the worker
worker.onmessage = function(e){
  // The message from the client:
  e.data
};
 
worker.postMessage("start");

The Client:

onmessage = function(e){
  if ( e.data === "start" ) {
    // Do some computation
    done()
  }
};
 
function done(){
  // Send back the results to the parent page
  postMessage("done");
}

This particular message-passing limitation is in place for a number of reasons: It keeps the child worker running securely (since it can’t, blatantly, affect a parent script) and it keeps the parent page thread-safe (having the DOM be thread safe would be a logistical nightmare for browser developers).

Right now Web Workers are implemented by Firefox 3.5 and Safari 4. They’ve also landed in the latest Chromium nightlies. Most people would balk when hearing this (only two released browsers!) but this shouldn’t be a concern. Workers allow you to take a normal piece of computation and highly parallelize it. In this way you can easily have two versions of a script (one that runs in older browsers and one that runs in a worker, if it’s available). Newer browsers will just run that much faster.

Some interesting demos have already been created that utilize this new API.

RayTracing

 

 

 

 

This demo makes use of Canvas to draw out a rendered scene. You’ll note that when you turn on the workers the scene is drawn in pieces. This is working by telling a worker to compute a slice of pixels. The worker responds with an array of colors to draw on the Canvas and the parent page changes the canvas. (Note that the worker itself doesn’t manipulate the canvas.)

Movement Tracking

 

 

 

(Requires Firefox 3.5. About the demo.) This one uses a number of technologies: The video element, the canvas element, and drawing video frames to a canvas. All of the motion detection it taking place in the background worker (so that the video rendering isn’t blocked).

Simulated Annealing

 

 

 

This demo attempts to draw outlines around a series of randomly-placed points using simulated annealing (More information). It also includes an animated PNG (works in Firefox 3.5) that continues to spin even while all the processing is occurring in the background.

Computing with JavaScript Web Workers

The other day Engine Yard started an interesting contest (which is probably over, by the time that you’re reading this).

The premise is that they would give you a phrase, which you would take the SHA1 of, and try to find another SHA1-ed string that has the smallest possible hamming distance from the original.

The phrase was posted the other day and developers have been furiously working to find a string that yields a low value.

The current leader is using a series of dedicated GPUs crunching out results at a pace of a couple hundred million per second. Considering the rate at which they’re progressing any other implementation will have a hard time catching up.

Of greater interest to me were two pure-JavaScript (12) entrants into the competition – they both run completely in the browser and utilize the user’s JavaScript engine to find results. While neither of them have a prayer of overcoming the GPU-powered monsters dominating the pack, they do serve as an interesting realm for exploration.

Reading through the source to both implementations they both utilize nearly-identical tactics for computing results: They execute a batch of results broken up by a timer. I’ve played around with them in different browsers and have been able to get around 1000-1500 matches/second. Unfortunately they both peg the CPU pretty hard and even with the timer splitting they manage to bog down the user interface.

This sounds like a perfect opportunity to use Web Workers!

I took the Ray C Morgan implementation, stripped out all the UI components and timers, and pushed it in to worker (through which 4 of them are run in parallel). (I submit results back to the original implementation, just in case a good result is found.)

Check out the demo and source:

I ran the old implementation against the new one in the browsers that support Web Workers to arrive at the following results:

Browser Old Runs/s New Runs/s
Firefox 3.5 2700 4600
Safari 4 2500 8400
Chrome Nightly 4500 9600

How does this implementation work? Digging in to the source of the parent launcherwe can see:

// Build a worker
var worker = new Worker(“worker.js”);

// Listen for incoming messages
worker.onmessage = function(e){
var parts = e.data.split(” “);

// We’re getting the rate at which computations are done
if ( parts[0] === “rate” ) {
rates[i] = parseInt(parts[1]);

// Total the rates from all the workers
var total = 0;
for ( var j = 0; j < rates.length; j++ ) { total += rates[j]; } num.innerHTML = total; // We've found a new best score, send it to the server } else if ( parts[0] === "found" ) { var img = document.createElement("img"); img.src = "http://www.raycmorgan.com/new-best?phrase=" + escape(parts.slice(1).join(" ")); document.body.appendChild( img ); // A new personal best score was found } else if ( parts[0] === "mybest" ) { var tmp = parseInt(parts[1]); if ( tmp < mybest ) { mybest = tmp; best.innerHTML = mybest; } } }; // Start the worker worker.postMessage( data.sha + " " + data.words.join(",") + " " + data.best );[/js] To start, we're constructing the worker and listening for any incoming messages. There are three types of messages that can come from the worker: "rate" (a 'ping' from the worker notifying the parent how quickly it's running), "found" (sent back when a new high scoring phrase has been found by the client), and "mybest" (sent when the worker gets a new personal-best high score). Additionally we can see the initialization data sent to the client in worker.postMessage. Unfortunately we have to pass the data in using a string in order to have it work in all browsers (only Firefox 3.5 supports the ability to pass in a raw JavaScript object).

Looking at the contents of the worker we can see some more, interesting, logic.

// … snip …

// New Personal Best Found
if (distance < myBest) { myBest = distance; postMessage("mybest " + myBest); } // New All-time Best Found if (distance < best) { best = distance; postMessage("found " + phrase); } // ... snip ... // Report Rate Back to Parent function stats() { var nowDiff = (new Date()).getTime() - startTime; var perSec = Math.floor(processed/nowDiff*1000); postMessage( "rate " + perSec ); } // ... snip ... // Get the incoming information from the parent onmessage = function(e){ var parts = e.data.split(" "); data = { sha: parts[0], words: parts[1].split(","), best: parts[2] }; start(); };[/js] The two 'distance' checks take place deep in the computation logic. After a new match has been found it is compared against the existing high scores. If this a sufficiently good-enough the result is sent back to the parent page using postMessage.

The ‘stats’ function is called periodically, which then reports back the current rate of processing to the parent page.

The ‘onmessage’ callback listens for the initialization data to come from the parent page – and once it’s been received begins processing.

In all I found this project to be a lot of fun – a relatively minor amount of code yielded 2-3x faster computation power. If you’re doing any computation with JavaScript you should definitely opt to use Web Workers if they’re available – the result is both faster and a better experience for the end user.

 

 

 

 

=================================

=================================

=================================

 

 

 

출처: https://developer.mozilla.org/ko/docs/Web/API/SharedWorker

 

SharedWorker

 

SharedWorker 인터페이스는 윈도우 창이나 iframe, 워커등의 다른 브라우징 컨텍스트에서도 접근이 가능한 특정 종류의 워커를 의미합니다. 기존의 다른 종류의 워커들과 다른 전역 스코프를 갖는 인터페이스를 구현합니다. SharedWorkerGlobalScope.

알아둘 점 :  SharedWorker 가 몇개의 다른 브라우징 컨텍스트에서 접근이 가능하면, 그 브라우징 컨텍스트들은 모두 정확히 같은 오리진을 공유해야 합니다. (같은 프로토콜, 호스트, 포트 등)

알아둘 점 : 파이어폭스에서,  shared workers 는 개인, 비개인 윈도우 간에 공유될 수 없습니다. (확인 bug 1177621.)

속성들Edit

EventTarget 의 속성들을 상속 받습니다. 그리고 AbstractWorker 의 속성들을 구현할 수 있습니다.

AbstractWorker.onerror

워커에서 ErrorEvent 타입의 에러가 발생했을 때 호출되는는 EventListener
SharedWorker.port Read only
shared worker를 제어하거나 통신하기 위해 사용되는 MessagePort 객체를 반환

생성자Edit

SharedWorker()
특정 URL에서 스크립트를 실행하는 shared web worker를 생성합니다.

메서드Edit

EventTarget 의 속성들을 상속 받습니다. 그리고 AbstractWorker 의 속성들을 구현할 수 있습니다.

예제Edit

Basic shared worker example (run shared worker) 를 보시면 2개의 HTML 페이지가 있습니다. 각각 간단한 계산을 위해 자바스크립트를 사용합니다. 각기 다른 스크립트가 계산을 위해 같은 워커 파일을 사용합니다 — 두 개 페이지가 모두 다른 윈도우창에서 실행되더라도 같은 워커에 접근할 수 있습니다.

아래 코드 스니펫은  SharedWorker() 생성자를 이용해 SharedWorker 객체를 생성합니다. 두 스크립트 모두 아래를 포함합니다.

var myWorker = new SharedWorker("worker.js");

두 스크립트는 SharedWorker.port 속성으로 생성한 MessagePort 객체를 통해 워커에 접근할 수 있습니다. addEventListener 를 이용하여 onmessage 가 추가되면, port는 start() 메서드를 이용하여 수동으로 시작할 수 있습니다.

myWorker.port.start();

포트가 시작되면, 양 스크립트는 워커에 메시지를 보내고 port.postMessage()와 port.onmessage 를 각각 이용하여  메시지를 처리합니다.

first.onchange = function() {     myWorker.port.postMessage([first.value,second.value]);     console.log('Message posted to worker');   }    second.onchange = function() {     myWorker.port.postMessage([first.value,second.value]);     console.log('Message posted to worker');   }    myWorker.port.onmessage = function(e) {     result1.textContent = e.data;     console.log('Message received from worker');   }

워커에서 SharedWorkerGlobalScope.onconnect 핸들러를 이용하여 위에 언급된 포트에 접속할 수 있습니다. 워커에 연관되어 있는 포트는 connect 이벤트 포트 속성에 접근할 수 있습니다 — 그리고나서 MessagePort start() 메서드로 포트를 시작하고, onmessage 핸들러로 메인쓰레드에서 받은 메시지를 처리합니다.

onconnect = function(e) {     var port = e.ports[0];      port.addEventListener('message', function(e) {       var workerResult = 'Result: ' + (e.data[0] * e.data[1]);       port.postMessage(workerResult);     });      port.start(); // Required when using addEventListener. Otherwise called implicitly by onmessage setter. }

Specifications

 

 

 

 

=================================

=================================

=================================

 

 

 

출처: https://html.spec.whatwg.org/#toc-workers

 

10 Web workers

10.1 Introduction

10.1.1 Scope

This section is non-normative.

This specification defines an API for running scripts in the background independently of any user interface scripts.

This allows for long-running scripts that are not interrupted by scripts that respond to clicks or other user interactions, and allows long tasks to be executed without yielding to keep the page responsive.

Workers (as these background scripts are called herein) are relatively heavy-weight, and are not intended to be used in large numbers. For example, it would be inappropriate to launch one worker for each pixel of a four megapixel image. The examples below show some appropriate uses of workers.

Generally, workers are expected to be long-lived, have a high start-up performance cost, and a high per-instance memory cost.

10.1.2 Examples

This section is non-normative.

There are a variety of uses that workers can be put to. The following subsections show various examples of this use.

10.1.2.1 A background number-crunching worker

This section is non-normative.

The simplest use of workers is for performing a computationally expensive task without interrupting the user interface.

In this example, the main document spawns a worker to (naïvely) compute prime numbers, and progressively displays the most recently found prime number.

The main page is as follows:

<!DOCTYPE HTML> <html>  <head>   <meta charset="utf-8">   <title>Worker example: One-core computation</title>  </head>  <body>   <p>The highest prime number discovered so far is: <output id="result"></output></p>   <script>    var worker = new Worker('worker.js');    worker.onmessage = function (event) {      document.getElementById('result').textContent = event.data;    };   </script>  </body> </html> 

The Worker() constructor call creates a worker and returns a Worker object representing that worker, which is used to communicate with the worker. That object's onmessageevent handler allows the code to receive messages from the worker.

The worker itself is as follows:

var n = 1; search: while (true) {   n += 1;   for (var i = 2; i <= Math.sqrt(n); i += 1)     if (n % i == 0)      continue search;   // found a prime!   postMessage(n); } 

The bulk of this code is simply an unoptimized search for a prime number. The postMessage() method is used to send a message back to the page when a prime is found.

View this example online.

10.1.2.2 Using a JavaScript module as a worker

This section is non-normative.

All of our examples so far show workers that run classic scripts. Workers can instead be instantiated using module scripts, which have the usual benefits: the ability to use the JavaScript import statement to import other modules; strict mode by default; and top-level declarations not polluting the worker's global scope.

Note that such module-based workers follow different restrictions regarding cross-origin content, compared to classic workers. Unlike classic workers, module workers can be instantiated using a cross-origin script, as long as that script is exposed using the CORS protocol. Additionally, the importScripts() method will automatically fail inside module workers; the JavaScript import statement is generally a better choice.

In this example, the main document uses a worker to do off-main-thread image manipulation. It imports the filters used from another module.

The main page is as follows:

<!DOCTYPE html> <meta charset="utf-8"> <title>Worker example: image decoding</title>  <p>   <label>     Type an image URL to decode     <input type="url" id="image-url" list="image-list">     <datalist id="image-list">       <option value="https://html.spec.whatwg.org/images/drawImage.png">       <option value="https://html.spec.whatwg.org/images/robots.jpeg">       <option value="https://html.spec.whatwg.org/images/arcTo2.png">     </datalist>   </label> </p>  <p>   <label>     Choose a filter to apply     <select id="filter">       <option value="none">none</option>       <option value="grayscale">grayscale</option>       <option value="brighten">brighten by 20%</option>     </select>   </label> </p>  <canvas id="output"></canvas>  <script type="module">   const worker = new Worker("worker.js", { type: "module" });   worker.onmessage = receiveFromWorker;    const url = document.querySelector("#image-url");   const filter = document.querySelector("#filter");   const output = document.querySelector("#output");    url.oninput = updateImage;   filter.oninput = sendToWorker;    let imageData, context;    function updateImage() {     const img = new Image();     img.src = url.value;      img.onload = () => {       output.innerHTML = "";        const canvas = document.createElement("canvas");       canvas.width = img.width;       canvas.height = img.height;        context = canvas.getContext("2d");       context.drawImage(img, 0, 0);       imageData = context.getImageData(0, 0, canvas.width, canvas.height);        sendToWorker();       output.appendChild(canvas);     };   }    function sendToWorker() {     worker.postMessage({ imageData, filter: filter.value });   }    function receiveFromWorker(e) {     context.putImageData(e.data, 0, 0);   } </script> 

The worker file is then:

import * as filters from "./filters.js";  self.onmessage = e => {   const { imageData, filter } = e.data;   filters[filter](imageData);   self.postMessage(imageData, [imageData.data.buffer]); }; 

Which imports the file filters.js:

export function none() {}  export function grayscale({ data: d }) {   for (let i = 0; i < d.length; i += 4) {     const [r, g, b] = [d[i], d[i + 1], d[i + 2]];      // CIE luminance for the RGB     // The human eye is bad at seeing red and blue, so we de-emphasize them.     d[i] = d[i + 1] = d[i + 2] = 0.2126 * r + 0.7152 * g + 0.0722 * b;   } };  export function brighten({ data: d }) {   for (let i = 0; i < d.length; ++i) {     d[i] *= 1.2;   } }; 

View this example online.

10.1.2.3 Shared workers introduction

Support: sharedworkersChrome for AndroidNoneChrome4+iOS SafariNoneUC Browser for Android11.8+Firefox29+IENoneOpera MiniNoneSamsung InternetNoneSafariNoneEdgeNoneAndroid BrowserNoneOpera10.6+

Source: caniuse.com

This section is non-normative.

This section introduces shared workers using a Hello World example. Shared workers use slightly different APIs, since each worker can have multiple connections.

This first example shows how you connect to a worker and how a worker can send a message back to the page when it connects to it. Received messages are displayed in a log.

Here is the HTML page:

<!DOCTYPE HTML> <meta charset="utf-8"> <title>Shared workers: demo 1</title> <pre id="log">Log:</pre> <script>   var worker = new SharedWorker('test.js');   var log = document.getElementById('log');   worker.port.onmessage = function(e) { // note: not worker.onmessage!     log.textContent += '\n' + e.data;   } </script> 

Here is the JavaScript worker:

onconnect = function(e) {   var port = e.ports[0];   port.postMessage('Hello World!'); } 

View this example online.


This second example extends the first one by changing two things: first, messages are received using addEventListener() instead of an event handler IDL attribute, and second, a message is sent to the worker, causing the worker to send another message in return. Received messages are again displayed in a log.

Here is the HTML page:

<!DOCTYPE HTML> <meta charset="utf-8"> <title>Shared workers: demo 2</title> <pre id="log">Log:</pre> <script>   var worker = new SharedWorker('test.js');   var log = document.getElementById('log');   worker.port.addEventListener('message', function(e) {     log.textContent += '\n' + e.data;   }, false);   worker.port.start(); // note: need this when using addEventListener   worker.port.postMessage('ping'); </script> 

Here is the JavaScript worker:

onconnect = function(e) {   var port = e.ports[0];   port.postMessage('Hello World!');   port.onmessage = function(e) {     port.postMessage('pong'); // not e.ports[0].postMessage!     // e.target.postMessage('pong'); would work also   } } 

View this example online.


Finally, the example is extended to show how two pages can connect to the same worker; in this case, the second page is merely in an iframe on the first page, but the same principle would apply to an entirely separate page in a separate top-level browsing context.

Here is the outer HTML page:

<!DOCTYPE HTML> <meta charset="utf-8"> <title>Shared workers: demo 3</title> <pre id="log">Log:</pre> <script>   var worker = new SharedWorker('test.js');   var log = document.getElementById('log');   worker.port.addEventListener('message', function(e) {     log.textContent += '\n' + e.data;   }, false);   worker.port.start();   worker.port.postMessage('ping'); </script> <iframe src="inner.html"></iframe> 

Here is the inner HTML page:

<!DOCTYPE HTML> <meta charset="utf-8"> <title>Shared workers: demo 3 inner frame</title> <pre id=log>Inner log:</pre> <script>   var worker = new SharedWorker('test.js');   var log = document.getElementById('log');   worker.port.onmessage = function(e) {    log.textContent += '\n' + e.data;   } </script> 

Here is the JavaScript worker:

var count = 0; onconnect = function(e) {   count += 1;   var port = e.ports[0];   port.postMessage('Hello World! You are connection #' + count);   port.onmessage = function(e) {     port.postMessage('pong');   } } 

View this example online.

10.1.2.4 Shared state using a shared worker

This section is non-normative.

In this example, multiple windows (viewers) can be opened that are all viewing the same map. All the windows share the same map information, with a single worker coordinating all the viewers. Each viewer can move around independently, but if they set any data on the map, all the viewers are updated.

The main page isn't interesting, it merely provides a way to open the viewers:

<!DOCTYPE HTML> <html>  <head>   <meta charset="utf-8">   <title>Workers example: Multiviewer</title>   <script>    function openViewer() {      window.open('viewer.html');    }   </script>  </head>  <body>   <p><button type=button onclick="openViewer()">Open a new   viewer</button></p>   <p>Each viewer opens in a new window. You can have as many viewers   as you like, they all view the same data.</p>  </body> </html> 

The viewer is more involved:

<!DOCTYPE HTML> <html>  <head>   <meta charset="utf-8">   <title>Workers example: Multiviewer viewer</title>   <script>    var worker = new SharedWorker('worker.js', 'core');     // CONFIGURATION    function configure(event) {      if (event.data.substr(0, 4) != 'cfg ') return;      var name = event.data.substr(4).split(' ', 1)[0];      // update display to mention our name is name      document.getElementsByTagName('h1')[0].textContent += ' ' + name;      // no longer need this listener      worker.port.removeEventListener('message', configure, false);    }    worker.port.addEventListener('message', configure, false);     // MAP    function paintMap(event) {      if (event.data.substr(0, 4) != 'map ') return;      var data = event.data.substr(4).split(',');      // display tiles data[0] .. data[8]      var canvas = document.getElementById('map');      var context = canvas.getContext('2d');      for (var y = 0; y < 3; y += 1) {        for (var x = 0; x < 3; x += 1) {          var tile = data[y * 3 + x];          if (tile == '0')            context.fillStyle = 'green';          else            context.fillStyle = 'maroon';          context.fillRect(x * 50, y * 50, 50, 50);        }      }    }    worker.port.addEventListener('message', paintMap, false);     // PUBLIC CHAT    function updatePublicChat(event) {      if (event.data.substr(0, 4) != 'txt ') return;      var name = event.data.substr(4).split(' ', 1)[0];      var message = event.data.substr(4 + name.length + 1);      // display "<name> message" in public chat      var public = document.getElementById('public');      var p = document.createElement('p');      var n = document.createElement('button');      n.textContent = '<' + name + '> ';      n.onclick = function () { worker.port.postMessage('msg ' + name); };      p.appendChild(n);      var m = document.createElement('span');      m.textContent = message;      p.appendChild(m);      public.appendChild(p);    }    worker.port.addEventListener('message', updatePublicChat, false);     // PRIVATE CHAT    function startPrivateChat(event) {      if (event.data.substr(0, 4) != 'msg ') return;      var name = event.data.substr(4).split(' ', 1)[0];      var port = event.ports[0];      // display a private chat UI      var ul = document.getElementById('private');      var li = document.createElement('li');      var h3 = document.createElement('h3');      h3.textContent = 'Private chat with ' + name;      li.appendChild(h3);      var div = document.createElement('div');      var addMessage = function(name, message) {        var p = document.createElement('p');        var n = document.createElement('strong');        n.textContent = '<' + name + '> ';        p.appendChild(n);        var t = document.createElement('span');        t.textContent = message;        p.appendChild(t);        div.appendChild(p);      };      port.onmessage = function (event) {        addMessage(name, event.data);      };      li.appendChild(div);      var form = document.createElement('form');      var p = document.createElement('p');      var input = document.createElement('input');      input.size = 50;      p.appendChild(input);      p.appendChild(document.createTextNode(' '));      var button = document.createElement('button');      button.textContent = 'Post';      p.appendChild(button);      form.onsubmit = function () {        port.postMessage(input.value);        addMessage('me', input.value);        input.value = '';        return false;      };      form.appendChild(p);      li.appendChild(form);      ul.appendChild(li);    }    worker.port.addEventListener('message', startPrivateChat, false);     worker.port.start();   </script>  </head>  <body>   <h1>Viewer</h1>   <h2>Map</h2>   <p><canvas id="map" height=150 width=150></canvas></p>   <p>    <button type=button onclick="worker.port.postMessage('mov left')">Left</button>    <button type=button onclick="worker.port.postMessage('mov up')">Up</button>    <button type=button onclick="worker.port.postMessage('mov down')">Down</button>    <button type=button onclick="worker.port.postMessage('mov right')">Right</button>    <button type=button onclick="worker.port.postMessage('set 0')">Set 0</button>    <button type=button onclick="worker.port.postMessage('set 1')">Set 1</button>   </p>   <h2>Public Chat</h2>   <div id="public"></div>   <form onsubmit="worker.port.postMessage('txt ' + message.value); message.value = ''; return false;">    <p>     <input type="text" name="message" size="50">     <button>Post</button>    </p>   </form>   <h2>Private Chat</h2>   <ul id="private"></ul>  </body> </html> 

There are several key things worth noting about the way the viewer is written.

Multiple listeners. Instead of a single message processing function, the code here attaches multiple event listeners, each one performing a quick check to see if it is relevant for the message. In this example it doesn't make much difference, but if multiple authors wanted to collaborate using a single port to communicate with a worker, it would allow for independent code instead of changes having to all be made to a single event handling function.

Registering event listeners in this way also allows you to unregister specific listeners when you are done with them, as is done with the configure() method in this example.

Finally, the worker:

var nextName = 0; function getNextName() {   // this could use more friendly names   // but for now just return a number   return nextName++; }  var map = [  [0, 0, 0, 0, 0, 0, 0],  [1, 1, 0, 1, 0, 1, 1],  [0, 1, 0, 1, 0, 0, 0],  [0, 1, 0, 1, 0, 1, 1],  [0, 0, 0, 1, 0, 0, 0],  [1, 0, 0, 1, 1, 1, 1],  [1, 1, 0, 1, 1, 0, 1], ];  function wrapX(x) {   if (x < 0) return wrapX(x + map[0].length);   if (x >= map[0].length) return wrapX(x - map[0].length);   return x; }  function wrapY(y) {   if (y < 0) return wrapY(y + map.length);   if (y >= map[0].length) return wrapY(y - map.length);   return y; }  function wrap(val, min, max) {   if (val < min)     return val + (max-min)+1;   if (val > max)     return val - (max-min)-1;   return val; }  function sendMapData(viewer) {   var data = '';   for (var y = viewer.y-1; y <= viewer.y+1; y += 1) {     for (var x = viewer.x-1; x <= viewer.x+1; x += 1) {       if (data != '')         data += ',';       data += map[wrap(y, 0, map[0].length-1)][wrap(x, 0, map.length-1)];     }   }   viewer.port.postMessage('map ' + data); }  var viewers = {}; onconnect = function (event) {   var name = getNextName();   event.ports[0]._data = { port: event.ports[0], name: name, x: 0, y: 0, };   viewers[name] = event.ports[0]._data;   event.ports[0].postMessage('cfg ' + name);   event.ports[0].onmessage = getMessage;   sendMapData(event.ports[0]._data); };  function getMessage(event) {   switch (event.data.substr(0, 4)) {     case 'mov ':       var direction = event.data.substr(4);       var dx = 0;       var dy = 0;       switch (direction) {         case 'up': dy = -1; break;         case 'down': dy = 1; break;         case 'left': dx = -1; break;         case 'right': dx = 1; break;       }       event.target._data.x = wrapX(event.target._data.x + dx);       event.target._data.y = wrapY(event.target._data.y + dy);       sendMapData(event.target._data);       break;     case 'set ':       var value = event.data.substr(4);       map[event.target._data.y][event.target._data.x] = value;       for (var viewer in viewers)         sendMapData(viewers[viewer]);       break;     case 'txt ':       var name = event.target._data.name;       var message = event.data.substr(4);       for (var viewer in viewers)         viewers[viewer].port.postMessage('txt ' + name + ' ' + message);       break;     case 'msg ':       var party1 = event.target._data;       var party2 = viewers[event.data.substr(4).split(' ', 1)[0]];       if (party2) {         var channel = new MessageChannel();         party1.port.postMessage('msg ' + party2.name, [channel.port1]);         party2.port.postMessage('msg ' + party1.name, [channel.port2]);       }       break;   } } 

Connecting to multiple pages. The script uses the onconnect event listener to listen for multiple connections.

Direct channels. When the worker receives a "msg" message from one viewer naming another viewer, it sets up a direct connection between the two, so that the two viewers can communicate directly without the worker having to proxy all the messages.

View this example online.

10.1.2.5 Delegation

This section is non-normative.

With multicore CPUs becoming prevalent, one way to obtain better performance is to split computationally expensive tasks amongst multiple workers. In this example, a computationally expensive task that is to be performed for every number from 1 to 10,000,000 is farmed out to ten subworkers.

The main page is as follows, it just reports the result:

<!DOCTYPE HTML> <html>  <head>   <meta charset="utf-8">   <title>Worker example: Multicore computation</title>  </head>  <body>   <p>Result: <output id="result"></output></p>   <script>    var worker = new Worker('worker.js');    worker.onmessage = function (event) {      document.getElementById('result').textContent = event.data;    };   </script>  </body> </html> 

The worker itself is as follows:

// settings var num_workers = 10; var items_per_worker = 1000000;  // start the workers var result = 0; var pending_workers = num_workers; for (var i = 0; i < num_workers; i += 1) {   var worker = new Worker('core.js');   worker.postMessage(i * items_per_worker);   worker.postMessage((i+1) * items_per_worker);   worker.onmessage = storeResult; }  // handle the results function storeResult(event) {   result += 1*event.data;   pending_workers -= 1;   if (pending_workers <= 0)     postMessage(result); // finished! } 

It consists of a loop to start the subworkers, and then a handler that waits for all the subworkers to respond.

The subworkers are implemented as follows:

var start; onmessage = getStart; function getStart(event) {   start = 1*event.data;   onmessage = getEnd; }  var end; function getEnd(event) {   end = 1*event.data;   onmessage = null;   work(); }  function work() {   var result = 0;   for (var i = start; i < end; i += 1) {     // perform some complex calculation here     result += 1;   }   postMessage(result);   close(); } 

They receive two numbers in two events, perform the computation for the range of numbers thus specified, and then report the result back to the parent.

View this example online.

10.1.2.6 Providing libraries

This section is non-normative.

Suppose that a cryptography library is made available that provides three tasks:

Generate a public/private key pair
Takes a port, on which it will send two messages, first the public key and then the private key.
Given a plaintext and a public key, return the corresponding ciphertext
Takes a port, to which any number of messages can be sent, the first giving the public key, and the remainder giving the plaintext, each of which is encrypted and then sent on that same channel as the ciphertext. The user can close the port when it is done encrypting content.
Given a ciphertext and a private key, return the corresponding plaintext
Takes a port, to which any number of messages can be sent, the first giving the private key, and the remainder giving the ciphertext, each of which is decrypted and then sent on that same channel as the plaintext. The user can close the port when it is done decrypting content.

The library itself is as follows:

function handleMessage(e) {   if (e.data == "genkeys")     genkeys(e.ports[0]);   else if (e.data == "encrypt")     encrypt(e.ports[0]);   else if (e.data == "decrypt")     decrypt(e.ports[0]); }  function genkeys(p) {   var keys = _generateKeyPair();   p.postMessage(keys[0]);   p.postMessage(keys[1]); }  function encrypt(p) {   var key, state = 0;   p.onmessage = function (e) {     if (state == 0) {       key = e.data;       state = 1;     } else {       p.postMessage(_encrypt(key, e.data));     }   }; }  function decrypt(p) {   var key, state = 0;   p.onmessage = function (e) {     if (state == 0) {       key = e.data;       state = 1;     } else {       p.postMessage(_decrypt(key, e.data));     }   }; }  // support being used as a shared worker as well as a dedicated worker if ('onmessage' in this) // dedicated worker   onmessage = handleMessage; else // shared worker   onconnect = function (e) { e.port.onmessage = handleMessage; }   // the "crypto" functions:  function _generateKeyPair() {   return [Math.random(), Math.random()]; }  function _encrypt(k, s) {   return 'encrypted-' + k + ' ' + s; }  function _decrypt(k, s) {   return s.substr(s.indexOf(' ')+1); } 

Note that the crypto functions here are just stubs and don't do real cryptography.

This library could be used as follows:

<!DOCTYPE HTML> <html>  <head>   <meta charset="utf-8">   <title>Worker example: Crypto library</title>   <script>    const cryptoLib = new Worker('libcrypto-v1.js'); // or could use 'libcrypto-v2.js'    function startConversation(source, message) {      const messageChannel = new MessageChannel();      source.postMessage(message, [messageChannel.port2]);      return messageChannel.port1;    }    function getKeys() {      let state = 0;      startConversation(cryptoLib, "genkeys").onmessage = function (e) {        if (state === 0)          document.getElementById('public').value = e.data;        else if (state === 1)          document.getElementById('private').value = e.data;        state += 1;      };    }    function enc() {      const port = startConversation(cryptoLib, "encrypt");      port.postMessage(document.getElementById('public').value);      port.postMessage(document.getElementById('input').value);      port.onmessage = function (e) {        document.getElementById('input').value = e.data;        port.close();      };    }    function dec() {      const port = startConversation(cryptoLib, "decrypt");      port.postMessage(document.getElementById('private').value);      port.postMessage(document.getElementById('input').value);      port.onmessage = function (e) {        document.getElementById('input').value = e.data;        port.close();      };    }   </script>   <style>    textarea { display: block; }   </style>  </head>  <body onload="getKeys()">   <fieldset>    <legend>Keys</legend>    <p><label>Public Key: <textarea id="public"></textarea></label></p>    <p><label>Private Key: <textarea id="private"></textarea></label></p>   </fieldset>   <p><label>Input: <textarea id="input"></textarea></label></p>   <p><button onclick="enc()">Encrypt</button> <button onclick="dec()">Decrypt</button></p>  </body> </html> 

A later version of the API, though, might want to offload all the crypto work onto subworkers. This could be done as follows:

function handleMessage(e) {   if (e.data == "genkeys")     genkeys(e.ports[0]);   else if (e.data == "encrypt")     encrypt(e.ports[0]);   else if (e.data == "decrypt")     decrypt(e.ports[0]); }  function genkeys(p) {   var generator = new Worker('libcrypto-v2-generator.js');   generator.postMessage('', [p]); }  function encrypt(p) {   p.onmessage = function (e) {     var key = e.data;     var encryptor = new Worker('libcrypto-v2-encryptor.js');     encryptor.postMessage(key, [p]);   }; }  function encrypt(p) {   p.onmessage = function (e) {     var key = e.data;     var decryptor = new Worker('libcrypto-v2-decryptor.js');     decryptor.postMessage(key, [p]);   }; }  // support being used as a shared worker as well as a dedicated worker if ('onmessage' in this) // dedicated worker   onmessage = handleMessage; else // shared worker   onconnect = function (e) { e.ports[0].onmessage = handleMessage }; 

The little subworkers would then be as follows.

For generating key pairs:

onmessage = function (e) {   var k = _generateKeyPair();   e.ports[0].postMessage(k[0]);   e.ports[0].postMessage(k[1]);   close(); }  function _generateKeyPair() {   return [Math.random(), Math.random()]; } 

For encrypting:

onmessage = function (e) {   var key = e.data;   e.ports[0].onmessage = function (e) {     var s = e.data;     postMessage(_encrypt(key, s));   } }  function _encrypt(k, s) {   return 'encrypted-' + k + ' ' + s; } 

For decrypting:

onmessage = function (e) {   var key = e.data;   e.ports[0].onmessage = function (e) {     var s = e.data;     postMessage(_decrypt(key, s));   } }  function _decrypt(k, s) {   return s.substr(s.indexOf(' ')+1); } 

Notice how the users of the API don't have to even know that this is happening — the API hasn't changed; the library can delegate to subworkers without changing its API, even though it is accepting data using message channels.

View this example online.

10.1.3 Tutorials

10.1.3.1 Creating a dedicated worker

This section is non-normative.

Creating a worker requires a URL to a JavaScript file. The Worker() constructor is invoked with the URL to that file as its only argument; a worker is then created and returned:

var worker = new Worker('helper.js');

If you want your worker script to be interpreted as a module script instead of the default classic script, you need to use a slightly different signature:

var worker = new Worker('helper.js', { type: "module" });
10.1.3.2 Communicating with a dedicated worker

This section is non-normative.

Dedicated workers use MessagePort objects behind the scenes, and thus support all the same features, such as sending structured data, transferring binary data, and transferring other ports.

To receive messages from a dedicated worker, use the onmessage event handler IDL attribute on the Worker object:

worker.onmessage = function (event) { ... };

You can also use the addEventListener() method.

The implicit MessagePort used by dedicated workers has its port message queue implicitly enabled when it is created, so there is no equivalent to the MessagePortinterface's start() method on the Worker interface.

To send data to a worker, use the postMessage() method. Structured data can be sent over this communication channel. To send ArrayBuffer objects efficiently (by transferring them rather than cloning them), list them in an array in the second argument.

worker.postMessage({   operation: 'find-edges',   input: buffer, // an ArrayBuffer object   threshold: 0.6, }, [buffer]);

To receive a message inside the worker, the onmessage event handler IDL attribute is used.

onmessage = function (event) { ... };

You can again also use the addEventListener() method.

In either case, the data is provided in the event object's data attribute.

To send messages back, you again use postMessage(). It supports the structured data in the same manner.

postMessage(event.data.input, [event.data.input]); // transfer the buffer back
10.1.3.3 Shared workers

This section is non-normative.

Shared workers are identified by the URL of the script used to create it, optionally with an explicit name. The name allows multiple instances of a particular shared worker to be started.

Shared workers are scoped by origin. Two different sites using the same names will not collide. However, if a page tries to use the same shared worker name as another page on the same site, but with a different script URL, it will fail.

Creating shared workers is done using the SharedWorker() constructor. This constructor takes the URL to the script to use for its first argument, and the name of the worker, if any, as the second argument.

var worker = new SharedWorker('service.js');

Communicating with shared workers is done with explicit MessagePort objects. The object returned by the SharedWorker() constructor holds a reference to the port on its port attribute.

worker.port.onmessage = function (event) { ... }; worker.port.postMessage('some message'); worker.port.postMessage({ foo: 'structured', bar: ['data', 'also', 'possible']});

Inside the shared worker, new clients of the worker are announced using the connect event. The port for the new client is given by the event object's source attribute.

onconnect = function (event) {   var newPort = event.source;   // set up a listener   newPort.onmessage = function (event) { ... };   // send a message back to the port   newPort.postMessage('ready!'); // can also send structured data, of course };

10.2 Infrastructure

There are two kinds of workers; dedicated workers, and shared workers. Dedicated workers, once created, are linked to their creator; but message ports can be used to communicate from a dedicated worker to multiple other browsing contexts or workers. Shared workers, on the other hand, are named, and once created any script running in the same origin can obtain a reference to that worker and communicate with it.

10.2.1 The global scope

The global scope is the "inside" of a worker.

10.2.1.1 The WorkerGlobalScope common interface
[Exposed=Worker] interface WorkerGlobalScope : EventTarget {   readonly attribute WorkerGlobalScope self;   readonly attribute WorkerLocation location;   readonly attribute WorkerNavigator navigator;   void importScripts(USVString... urls);    attribute OnErrorEventHandler onerror;   attribute EventHandler onlanguagechange;   attribute EventHandler onoffline;   attribute EventHandler ononline;   attribute EventHandler onrejectionhandled;   attribute EventHandler onunhandledrejection; };

WorkerGlobalScope serves as the base class for specific types of worker global scope objects, including DedicatedWorkerGlobalScopeSharedWorkerGlobalScope, and ServiceWorkerGlobalScope.

WorkerGlobalScope object has an associated 

owner set

 (a set of Document and WorkerGlobalScope objects). It is initially empty and populated when the worker is created or obtained.

It is a set, instead of a single owner, to accomodate SharedWorkerGlobalScope objects.

WorkerGlobalScope object has an associated 

worker set

 (a set of WorkerGlobalScope objects). It is initially empty and populated when the worker creates or obtains further workers.

WorkerGlobalScope object has an associated 

type

 ("classic" or "module"). It is set during creation.

WorkerGlobalScope object has an associated 

url

 (null or a URL). It is initially null.

WorkerGlobalScope object has an associated 

name

 (a string). It is set during creation.

The name can have different semantics for each subclass of WorkerGlobalScope. For DedicatedWorkerGlobalScope instances, it is simply a developer-supplied name, useful mostly for debugging purposes. For SharedWorkerGlobalScope instances, it allows obtaining a reference to a common shared worker via the SharedWorker()constructor. For ServiceWorkerGlobalScope objects, it doesn't make sense (and as such isn't exposed through the JavaScript API at all).

WorkerGlobalScope object has an associated 

HTTPS state

 (an HTTPS state value). It is initially "none".

WorkerGlobalScope object has an associated 

referrer policy

 (a referrer policy). It is initially the empty string.

WorkerGlobalScope object has an associated 

CSP list

, which is a CSP list containing all of the Content Security Policy objects active for the worker. It is initially an empty list.

WorkerGlobalScope object has an associated 

module map

. It is a module map, initially empty.

workerGlobal . self
Returns workerGlobal.
workerGlobal . location
Returns workerGlobal's WorkerLocation object.
workerGlobal . navigator
Returns workerGlobal's WorkerNavigator object.
workerGlobal . importScripts(urls...)
Fetches each URL in urls, executes them one-by-one in the order they are passed, and then returns (or throws if something went amiss).

The 

self

 attribute must return the WorkerGlobalScope object itself.

The 

location

 attribute must return the WorkerLocation object whose associated WorkerGlobalScope object is the WorkerGlobalScope object.

While the WorkerLocation object is created after the WorkerGlobalScope object, this is not problematic as it cannot be observed from script.


The following are the event handlers (and their corresponding event handler event types) that must be supported, as event handler IDL attributes, by objects implementing the WorkerGlobalScope interface:

Event handler Event handler event type
onerror error
onlanguagechange languagechange
onoffline offline
ononline online
onrejectionhandled rejectionhandled
onunhandledrejection unhandledrejection
10.2.1.2 Dedicated workers and the DedicatedWorkerGlobalScope interface
[Global=(Worker,DedicatedWorker),Exposed=DedicatedWorker] interface DedicatedWorkerGlobalScope : WorkerGlobalScope {   [Replaceable] readonly attribute DOMString name;    void postMessage(any message, optional sequence<object> transfer = []);    void close();    attribute EventHandler onmessage;   attribute EventHandler onmessageerror; };

DedicatedWorkerGlobalScope objects act as if they had an implicit MessagePort associated with them. This port is part of a channel that is set up when the worker is created, but it is not exposed. This object must never be garbage collected before the DedicatedWorkerGlobalScope object.

All messages received by that port must immediately be retargeted at the DedicatedWorkerGlobalScope object.

dedicatedWorkerGlobal . name
Returns dedicatedWorkerGlobal's name, i.e. the value given to the Worker constructor. Primarily useful for debugging.
dedicatedWorkerGlobal . postMessage(message [, transfer ])
Clones message and transmits it to the Worker object associated with dedicatedWorkerGlobaltransfer can be passed as a list of objects that are to be transferred rather than cloned.
dedicatedWorkerGlobal . close()
Aborts dedicatedWorkerGlobal.

The 

name

 attribute must return the DedicatedWorkerGlobalScope object's name. Its value represents the name given to the worker using the Worker constructor, used primarily for debugging purposes.

The 

postMessage()

 method on DedicatedWorkerGlobalScope objects must act as if, when invoked, it immediately invoked the method of the same name on the port, with the same arguments, and returned the same return value.

To 

close a worker

, given a 

workerGlobal

, run these steps:

  1. Discard any tasks that have been added to 
    workerGlobal
    's event loop's task queues.
  2. Set 
    workerGlobal
    's closing flag to true. (This prevents any further tasks from being queued.)

The 

close()

 method, when invoked, must close a worker with this DedicatedWorkerGlobalScope object.


The following are the event handlers (and their corresponding event handler event types) that must be supported, as event handler IDL attributes, by objects implementing the DedicatedWorkerGlobalScope interface:

Event handler Event handler event type
onmessage message
onmessageerror messageerror

For the purposes of the application cache networking model, a dedicated worker is an extension of the cache host from which it was created.

10.2.1.3 Shared workers and the SharedWorkerGlobalScope interface
[Global=(Worker,SharedWorker),Exposed=SharedWorker] interface SharedWorkerGlobalScope : WorkerGlobalScope {   [Replaceable] readonly attribute DOMString name;    void close();    attribute EventHandler onconnect; };

SharedWorkerGlobalScope object has an associated 

constructor origin

, and 

constructor url

. They are initialized when the SharedWorkerGlobalScope object is created, in the run a worker algorithm.

Shared workers receive message ports through connect events on their SharedWorkerGlobalScope object for each connection.

sharedWorkerGlobal . name
Returns sharedWorkerGlobal's name, i.e. the value given to the SharedWorker constructor. Multiple SharedWorker objects can correspond to the same shared worker (and SharedWorkerGlobalScope), by reusing the same name.
sharedWorkerGlobal . close()
Aborts sharedWorkerGlobal.

The 

name

 attribute must return the SharedWorkerGlobalScope object's name. Its value represents the name that can be used to obtain a reference to the worker using the SharedWorker constructor.

The 

close()

 method, when invoked, must close a worker with this SharedWorkerGlobalScope object.


The following are the event handlers (and their corresponding event handler event types) that must be supported, as event handler IDL attributes, by objects implementing the SharedWorkerGlobalScope interface:

Event handler Event handler event type
onconnect connect

10.2.2 The event loop

Each WorkerGlobalScope object has a distinct event loop, separate from those used by units of related similar-origin browsing contexts. This event loop has no associatedbrowsing context, and its task queues only have events, callbacks, and networking activity as tasks. These event loops are created by the run a worker algorithm.

Each WorkerGlobalScope object also has a 

closing

 flag, which must be initially false, but which can get set to true by the algorithms in the processing model section below.

Once the WorkerGlobalScope's closing flag is set to true, the event loop's task queues must discard any further tasks that would be added to them (tasks already on the queue are unaffected except where otherwise specified). Effectively, once the closing flag is true, timers stop firing, notifications for all pending background operations are dropped, etc.

10.2.3 The worker's lifetime

Workers communicate with other workers and with browsing contexts through message channels and their MessagePort objects.

Each WorkerGlobalScope object 

worker global scope

 has a list of 

the worker's ports

, which consists of all the MessagePort objects that are entangled with another port and that have one (but only one) port owned by 

worker global scope

. This list includes the implicit MessagePort in the case of dedicated workers.

Given an environment settings object 

o

 when creating or obtaining a worker, the 

relevant owner to add

 depends on the type of global object specified by 

o

. If 

o

 specifies a global object that is a WorkerGlobalScope object (i.e., if we are creating a nested worker), then the relevant owner is that global object. Otherwise, 

o

 specifies a global objectthat is a Window object, and the relevant owner is the responsible document specified by 

o

.


A worker is said to be a 

permissible worker

 if its WorkerGlobalScope's owner set is not empty or:

The second part of this definition allows a shared worker to survive for a short time while a page is loading, in case that page is going to contact the shared worker again. This can be used by user agents as a way to avoid the cost of restarting a shared worker used by a site when the user is navigating from page to page within that site.

A worker is said to be an 

active needed worker

 if any its owners are either Document objects that are fully active or active needed workers.

A worker is said to be a 

protected worker

 if it is an active needed worker and either it has outstanding timers, database transactions, or network connections, or its list of the worker's ports is not empty, or its WorkerGlobalScope is actually a SharedWorkerGlobalScope object (i.e. the worker is a shared worker).

A worker is said to be a 

suspendable worker

 if it is not an active needed worker but it is a permissible worker.

10.2.4 Processing model

When a user agent is to 

run a worker

 for a script with Worker or SharedWorker object 

worker

URL 

url

environment settings object 

outside settings

MessagePort 

outside port

, and a WorkerOptions dictionary 

options

, it must run the following steps.

  1. Create a separate parallel execution environment (i.e. a separate thread or process or equivalent construct), and run the rest of these steps in that context.
  2. For the purposes of timing APIs, this is the 
    official moment of creation
     of the worker.
  3. Let 
    is shared
     be true if 
    worker
     is a SharedWorker object, and false otherwise.
  4. Let 
    owner
     be the relevant owner to add given 
    outside settings
    .
  5. Let 
    parent worker global scope
     be null.
  6. If 
    owner
     is a WorkerGlobalScope object (i.e., we are creating a nested worker), then set 
    parent worker global scope
     to 
    owner
    .
  7. Call the JavaScript InitializeHostDefinedRealm() abstract operation with the following customizations:
  8. Let 
    realm execution context
     be the running JavaScript execution context.
  9. This is the JavaScript execution context created in the previous step.
  10. Let 
    worker global scope
     be the global object of 
    realm execution context
    's Realm component.
  11. This is the DedicatedWorkerGlobalScope or SharedWorkerGlobalScope object created when calling InitializeHostDefinedRealm.
  12. Set up a worker environment settings object with 
    realm execution context
     and 
    outside settings
    , and let 
    inside settings
     be the result.
  13. Set 
    worker global scope
    's name to the value of 
    options
    's name member.
  14. If 
    is shared
     is true, then:
    1. Set 
      worker global scope
      's constructor origin to 
      outside settings
      's origin.
    2. Set 
      worker global scope
      's constructor url to 
      url
      .
  15. Let 
    destination
     be "sharedworker" if 
    is shared
     is true, and "worker" otherwise.
  16. Obtain 
    script
     by switching on the value of 
    options
    's type member:
    "classic"
    Fetch a classic worker script given urloutside settingsdestination, and inside settings.
    "module"
    Fetch a module worker script graph given urloutside settingsdestination, the value of the credentials member of options, and inside settings.
    In both cases, to perform the fetch given 
    request
    , perform the following steps if the   flag is set:
    1. Set request's reserved client to inside settings.
    2. Fetch 
      request
      , and asynchronously wait to run the remaining steps as part of fetch's process response for the response 
      response
      .
    3. Set 
      worker global scope
      's url to 
      response
      's url.
    4. Set 
      worker global scope
      's HTTPS state to 
      response
      's HTTPS state.
    5. Set 
      worker global scope
      's referrer policy to the result of parsing the `Referrer-Policy` header of 
      response
      .
    6. Execute the Initialize a global object's CSP list algorithm on 
      worker global scope
       and 
      response
      [CSP]
    7. Asynchronously complete the perform the fetch steps with 
      response
      .
    If the algorithm asynchronously completes with null, queue a task to fire an event named error at 
    worker
    , and return. Otherwise, continue the rest of these steps after the algorithm's asynchronous completion, with 
    script
     being the asynchronous completion value.
  17. Associate 
    worker
     with 
    worker global scope
    .
  18. Create a new MessagePort object whose owner is 
    inside settings
    . Let 
    inside port
     be this new object.
  19. Associate 
    inside port
     with 
    worker global scope
    .
  20. Entangle 
    outside port
     and 
    inside port
    .
  21. Append 
    owner
     to 
    worker global scope
    's owner set.
  22. If 
    parent worker global scope
     is not null, then append 
    worker global scope
     to 
    parent worker global scope
    's worker set.
  23. Set 
    worker global scope
    's type to the value of the type member of 
    options
    .
  24. Create a new WorkerLocation object and associate it with 
    worker global scope
    .
  25. Closing orphan workers: Start monitoring the worker such that no sooner than it stops being a protected worker, and no later than it stops being a permissible worker
    worker global scope
    's closing flag is set to true.
  26. Suspending workers: Start monitoring the worker, such that whenever 
    worker global scope
    's closing flag is false and the worker is a suspendable worker, the user agent suspends execution of script in that worker until such time as either the closing flag switches to true or the worker stops being a suspendable worker.
  27. Set 
    inside settings
    's execution ready flag.
  28. If 
    script
     is a classic script, then run the classic script 
    script
    . Otherwise, it is a module scriptrun the module script 
    script
    .
  29. In addition to the usual possibilities of returning a value or failing due to an exception, this could be prematurely aborted by the terminate a worker algorithm defined below.
  30. Enable 
    outside port
    's port message queue.
  31. If 
    is shared
     is false, enable the port message queue of the worker's implicit port.
  32. If 
    is shared
     is true, then queue a task, using the DOM manipulation task source, to fire an event named connect at 
    worker global scope
    , using MessageEvent, with the data attribute initialized to the empty string, the ports attribute initialized to a new frozen array containing 
    inside port
    , and the source attribute initialized to 
    inside port
    .
  33. Enable the client message queue of the ServiceWorkerContainer object whose associated service worker client is 
    worker global scope
    's relevant settings object.
  34. Event loop: Run the responsible event loop specified by 
    inside settings
     until it is destroyed.The worker processing model remains on this step until the event loop is destroyed, which happens after the closing flag is set to true, as described in the event loop processing model.
  35. The handling of events or the execution of callbacks by tasks run by the event loop might get prematurely aborted by the terminate a worker algorithm defined below.
  36. Empty the 
    worker global scope
    's list of active timers.
  37. Disentangle all the ports in the list of the worker's ports.
  38. Empty 
    worker global scope
    's owner set.

When a user agent is to 

terminate a worker

 it must run the following steps in parallel with the worker's main loop (the "run a worker" processing model defined above):

  1. Set the worker's WorkerGlobalScope object's closing flag to true.
  2. If there are any tasks queued in the WorkerGlobalScope object's event loop's task queues, discard them without processing them.
  3. Abort the script currently running in the worker.
  4. If the worker's WorkerGlobalScope object is actually a DedicatedWorkerGlobalScope object (i.e. the worker is a dedicated worker), then empty the port message queue of the port that the worker's implicit port is entangled with.

User agents may invoke the terminate a worker algorithm when a worker stops being an active needed worker and the worker continues executing even after its closing flag was set to true.


The task source for the tasks mentioned above is the DOM manipulation task source.

10.2.5 Runtime script errors

Whenever an uncaught runtime script error occurs in one of the worker's scripts, if the error did not occur while handling a previous script error, the user agent must report the error for that script, with the position (line number and column number) where the error occurred, using the WorkerGlobalScope object as the target.

For shared workers, if the error is still not handled afterwards, the error may be reported to a developer console.

For dedicated workers, if the error is still not handled afterwards, the user agent must queue a task to run these steps:

  1. Let 
    notHandled
     be the result of firing an event named error at the Worker object associated with the worker, using ErrorEvent, with the cancelable attribute initialized to true, the messagefilenamelineno, and colno attributes initialized appropriately, and the error attribute initialized to null.
  2. If 
    notHandled
     is true, then the user agent must act as if the uncaught runtime script error had occurred in the global scope that the Worker object is in, thus repeating the entire runtime script error reporting process one level up.

If the implicit port connecting the worker to its Worker object has been disentangled (i.e. if the parent worker has been terminated), then the user agent must act as if the Worker object had no error event handler and as if that worker's onerror attribute was null, but must otherwise act as described above.

Thus, error reports propagate up to the chain of dedicated workers up to the original Document, even if some of the workers along this chain have been terminated and garbage collected.

The task source for the task mentioned above is the DOM manipulation task source.

10.2.6 Creating workers

10.2.6.1 The AbstractWorker mixin
interface mixin AbstractWorker {   attribute EventHandler onerror; };

The following are the event handlers (and their corresponding event handler event types) that must be supported, as event handler IDL attributes, by objects implementing the AbstractWorker interface:

Event handler Event handler event type
onerror error
10.2.6.2 Script settings for workers

When the user agent is required to 

set up a worker environment settings object

, given a JavaScript execution context 

execution context

 and environment settings object

outside settings

, it must run the following steps:

  1. Let 
    inherited responsible browsing context
     be 
    outside settings
    's responsible browsing context.
  2. Let 
    inherited origin
     be 
    outside settings
    's origin.
  3. Let 
    worker event loop
     be a newly created event loop.
  4. Let 
    realm
     be the value of 
    execution context
    's Realm component.
  5. Let 
    worker global scope
     be 
    realm
    's global object.
  6. Let 
    settings object
     be a new environment settings object whose algorithms are defined as follows:
    The realm execution context

    Return 

    execution context

    .

    The module map

    Return 

    worker global scope

    's module map.

    The responsible browsing context

    Return 

    inherited responsible browsing context

    .

    The responsible event loop

    Return 

    worker event loop

    .

    The responsible document

    Not applicable (the responsible event loop is not a browsing context event loop).

    The API URL character encoding

    Return UTF-8.

    The API base URL

    Return 

    worker global scope

    's url.

    The origin

    Return a unique opaque origin if 

    worker global scope

    's url's scheme is "data", and 

    inherited origin

     otherwise.

    The HTTPS state

    Return 

    worker global scope

    's HTTPS state.

    The referrer policy

    Return 

    worker global scope

    's referrer policy.

  7. Set 
    settings object
    's id to a new unique opaque string, 
    settings object
    's creation URL to 
    worker global scope
    's url
    settings object
    's target browsing context to null, and
    settings object
    's active service worker to null.
  8. Set 
    realm
    's [[HostDefined]] field to 
    settings object
    .
  9. Return 
    settings object
    .
10.2.6.3 Dedicated workers and the Worker interface
[Constructor(USVString scriptURL, optional WorkerOptions options), Exposed=(Window,Worker)] interface Worker : EventTarget {   void terminate();    void postMessage(any message, optional sequence<object> transfer = []);   attribute EventHandler onmessage;   attribute EventHandler onmessageerror; };  dictionary WorkerOptions {   WorkerType type = "classic";   RequestCredentials credentials = "omit"; // credentials is only used if type is "module"   DOMString name = ""; };  enum WorkerType { "classic", "module" };  Worker includes AbstractWorker;
worker = new Worker(scriptURL [, options ])
Returns a new Worker object. scriptURL will be fetched and executed in the background, creating a new global environment for which worker represents the communication channel. options can be used to define the name of that global environment via the name option, primarily for debugging purposes. It can also ensure this new global environment supports JavaScript modules (specify type: "module"), and if that is specified, can also be used to specify how scriptURL is fetched through the credentials option.
worker . terminate()
Aborts worker's associated global environment.
worker . postMessage(message [, transfer ])
Clones message and transmits it to worker's global environment. transfer can be passed as a list of objects that are to be transferred rather than cloned.

The 

terminate()

 method, when invoked, must cause the terminate a worker algorithm to be run on the worker with which the object is associated.

Worker objects act as if they had an implicit MessagePort associated with them. This port is part of a channel that is set up when the worker is created, but it is not exposed. This object must never be garbage collected before the Worker object.

All messages received by that port must immediately be retargeted at the Worker object.

The 

postMessage()

 method on Worker objects must act as if, when invoked, it immediately invoked the method of the same name on the port, with the same arguments, and returned the same return value.

The postMessage() method's first argument can be structured data:

worker.postMessage({opcode: 'activate', device: 1938, parameters: [23, 102]});

The following are the event handlers (and their corresponding event handler event types) that must be supported, as event handler IDL attributes, by objects implementing the Worker interface:

Event handler Event handler event type
onmessage message
onmessageerror messageerror

When the 

Worker(scriptURLoptions)

 constructor is invoked, the user agent must run the following steps:

  1. The user agent may throw a "SecurityError" DOMException if the request violates a policy decision (e.g. if the user agent is configured to not allow the page to start dedicated workers).
  2. Let 
    outside settings
     be the current settings object.
  3. Parse the 
    scriptURL
     argument relative to 
    outside settings
    .
  4. If this fails, throw a "SyntaxError" DOMException.
  5. Let 
    worker URL
     be the resulting URL record.
  6. Any same-origin URL (including blob: URLs) can be used. data: URLs can also be used, but they create a worker with an opaque origin.
  7. Let 
    worker
     be a new Worker object.
  8. Create a new MessagePort object whose owner is 
    outside settings
    . Let this be the 
    outside port
    .
  9. Associate the 
    outside port
     with 
    worker
    .
  10. Run this step in parallel:
    1. Run a worker given 
      worker
      worker URL
      outside settings
      outside port
      , and 
      options
      .
  11. Return 
    worker
    .
10.2.6.4 Shared workers and the SharedWorker interface
[Constructor(USVString scriptURL, optional (DOMString or WorkerOptions) options),  Exposed=(Window,Worker)] interface SharedWorker : EventTarget {   readonly attribute MessagePort port; }; SharedWorker includes AbstractWorker;
sharedWorker = new SharedWorker(scriptURL [, name ])
Returns a new SharedWorker object. scriptURL will be fetched and executed in the background, creating a new global environment for which sharedWorkerrepresents the communication channel. name can be used to define the name of that global environment.
sharedWorker = new SharedWorker(scriptURL [, options ])
Returns a new SharedWorker object. scriptURL will be fetched and executed in the background, creating a new global environment for which sharedWorkerrepresents the communication channel. options can be used to define the name of that global environment via the name option. It can also ensure this new global environment supports JavaScript modules (specify type: "module"), and if that is specified, can also be used to specify how scriptURL is fetched through the credentials option.
sharedWorker . port
Returns sharedWorker's MessagePort object which can be used to communicate with the global environment.

The 

port

 attribute must return the value it was assigned by the object's constructor. It represents the MessagePort for communicating with the shared worker.

A user agent has an associated 

shared worker manager

 which is the result of starting a new parallel queue.

Each user agent has a single shared worker manager for simplicity. Implementations could use one per origin; that would not be observably different and enables more concurrency.

When the 

SharedWorker(scriptURLoptions)

 constructor is invoked:

  1. Optionally, throw a "SecurityError" DOMException if the request violates a policy decision (e.g. if the user agent is configured to not allow the page to start shared workers).
  2. If 
    options
     is a DOMString, set 
    options
     to a new WorkerOptions dictionary whose name member is set to the value of 
    options
     and whose other members are set to their default values.
  3. Let 
    outside settings
     be the current settings object.
  4. Parse 
    scriptURL
     relative to 
    outside settings
    .
  5. If this fails, throw a "SyntaxError" DOMException.
  6. Otherwise, let 
    urlRecord
     be the resulting URL record.
  7. Any same-origin URL (including blob: URLs) can be used. data: URLs can also be used, but they create a worker with an opaque origin.
  8. Let 
    worker
     be a new SharedWorker object.
  9. Create a new MessagePort object whose owner is 
    outside settings
    . Let this be the 
    outside port
    .
  10. Assign 
    outside port
     to the port attribute of 
    worker
    .
  11. Let 
    callerIsSecureContext
     be the result of executing Is environment settings object a secure context? on 
    outside settings
    .
  12. Enqueue the following steps to the shared worker manager:
    1. Let 
      worker global scope
       be null.
    2. If there exists a SharedWorkerGlobalScope object whose closing flag is false, constructor origin is same origin with 
      outside settings
      's originconstructor url equals
      urlRecord
      , and name equals the value of 
      options
      's name member, then set 
      worker global scope
       to that SharedWorkerGlobalScope object.
    3. data: URLs create a worker with an opaque origin. Both the constructor origin and constructor url are compared so the same data: URL can be used within an origin to get to the same SharedWorkerGlobalScope object, but cannot be used to bypass the same origin restriction.
    4. If 
      worker global scope
       is not null, but the user agent has been configured to disallow communication between the worker represented by the 
      worker global scope
      and the scripts whose settings object is 
      outside settings
      , then set 
      worker global scope
       to null.
    5. For example, a user agent could have a development mode that isolates a particular top-level browsing context from all other pages, and scripts in that development mode could be blocked from connecting to shared workers running in the normal browser mode.
    6. If 
      worker global scope
       is not null, then run these subsubsteps:
      1. Let 
        settings object
         be the relevant settings object for 
        worker global scope
        .
      2. Let 
        workerIsSecureContext
         be the result of executing Is environment settings object a secure context? on 
        settings object
        .
      3. If 
        workerIsSecureContext
         is not 
        callerIsSecureContext
        , then queue a task to fire an event named error at 
        worker
         and abort these subsubsteps. [SECURE-CONTEXTS]
      4. Associate 
        worker
         with 
        worker global scope
        .
      5. Create a new MessagePort object whose owner is 
        settings object
        . Let this be the 
        inside port
        .
      6. Entangle 
        outside port
         and 
        inside port
        .
      7. Queue a task, using the DOM manipulation task source, to fire an event named connect at 
        worker global scope
        , using MessageEvent, with the data attribute initialized to the empty string, the ports attribute initialized to a new frozen array containing only 
        inside port
        , and the source attribute initialized to 
        inside port
        .
      8. Append the relevant owner to add given 
        outside settings
         to 
        worker global scope
        's owner set.
      9. If 
        outside settings
        's global object is a WorkerGlobalScope object, then append 
        worker global scope
         to 
        outside settings
        's global object's worker set.
    7. Otherwise, in parallelrun a worker given 
      worker
      urlRecord
      outside settings
      outside port
      , and 
      options
      .
  13. Return 
    worker
    .

Support:hardwareconcurrencyChrome for Android64+Chrome37+iOS Safari10.3+UC Browser for Android11.8+Firefox48+IENoneOpera MiniNoneSamsung Internet4+Safari10.1+Edge15+Android Browser62+Opera24+

Source: caniuse.com

interface mixin NavigatorConcurrentHardware {   readonly attribute unsigned long long hardwareConcurrency; };
self . navigator . hardwareConcurrency

Returns the number of logical processors potentially available to the user agent.

The 

navigator.hardwareConcurrency

 attribute's getter must return a number between 1 and the number of logical processors potentially available to the user agent. If this cannot be determined, the getter must return 1.(This is a fingerprinting vector.)

User agents should err toward exposing the number of logical processors available, using lower values only in cases where there are user-agent specific limits in place (such as a limitation on the number of workers that can be created) or when the user agent desires to limit fingerprinting possibilities.

10.3 APIs available to workers

10.3.1 Importing scripts and libraries

When a script invokes the 

importScripts(urls)

 method on a WorkerGlobalScope object, the user agent must import scripts into worker global scope given this WorkerGlobalScope object and 

urls

.

To 

import scripts into worker global scope

, given a WorkerGlobalScope object 

worker global scope

 and a sequence<DOMString> 

urls

, run these steps. The algorithm may optionally be customized by supplying custom perform the fetch hooks, which if provided will be used when invoking fetch a classic worker-imported script.

  1. If 
    worker global scope
    's type is "module", throw a TypeError exception.
  2. Let 
    settings object
     be the current settings object.
  3. If 
    urls
     is empty, return.
  4. Parse each value in 
    urls
     relative to 
    settings object
    . If any fail, throw a "SyntaxError" DOMException.
  5. For each 
    url
     in the resulting URL records, run these substeps:
    1. Fetch a classic worker-imported script given 
      url
       and 
      settings object
      , passing along any custom perform the fetch steps provided. If this succeeds, let 
      script
       be the result. Otherwise, rethrow the exception.
    2. Run the classic script 
      script
      , with the rethrow errors argument set to true.If an exception was thrown or if the script was prematurely aborted, then abort all these steps, letting the exception or aborting continue to be processed by the calling script.
    3. script
       will run until it either returns, fails to parse, fails to catch an exception, or gets prematurely aborted by the terminate a worker algorithm defined above.

Service Workers

 is an example of a specification that runs this algorithm with its own options for the perform the fetch hook. [SW]

10.3.2 The WorkerNavigator interface

The 

navigator

 attribute of the WorkerGlobalScope interface must return an instance of the WorkerNavigator interface, which represents the identity and state of the user agent (the client):

[Exposed=Worker] interface WorkerNavigator {}; WorkerNavigator includes NavigatorID; WorkerNavigator includes NavigatorLanguage; WorkerNavigator includes NavigatorOnLine; WorkerNavigator includes NavigatorConcurrentHardware;

10.3.3 The WorkerLocation interface

[Exposed=Worker] interface WorkerLocation {   stringifier readonly attribute USVString href;   readonly attribute USVString origin;   readonly attribute USVString protocol;   readonly attribute USVString host;   readonly attribute USVString hostname;   readonly attribute USVString port;   readonly attribute USVString pathname;   readonly attribute USVString search;   readonly attribute USVString hash; };

WorkerLocation object has an associated 

WorkerGlobalScope object

 (a WorkerGlobalScope object).

The 

href

 attribute's getter must return the associated WorkerGlobalScope object's urlserialized.

The 

origin

 attribute's getter must return the serialization of the associated WorkerGlobalScope object's url's origin.

The 

protocol

 attribute's getter must return the associated WorkerGlobalScope object's url's scheme, followed by ":".

The 

host

 attribute's getter must run these steps:

  1. Let 
    url
     be the associated WorkerGlobalScope object's url.
  2. If 
    url
    's host is null, return the empty string.
  3. If 
    url
    's port is null, return 
    url
    's hostserialized.
  4. Return 
    url
    's hostserialized, followed by ":" and 
    url
    's portserialized.

The 

hostname

 attribute's getter must run these steps:

  1. Let 
    host
     be the associated WorkerGlobalScope object's url's host.
  2. If 
    host
     is null, return the empty string.
  3. Return 
    host
    serialized.

The 

port

 attribute's getter must run these steps:

  1. Let 
    port
     be the associated WorkerGlobalScope object's url's port.
  2. If 
    port
     is null, return the empty string.
  3. Return 
    port
    serialized.

The 

pathname

 attribute's getter must run these steps:

  1. Let 
    url
     be the associated WorkerGlobalScope object's url.
  2. If 
    url
    's cannot-be-a-base-URL flag is set, return the first string in 
    url
    's path.
  3. Return "/", followed by the strings in 
    url
    's path (including empty strings), separated from each other by "/".

The 

search

 attribute's getter must run these steps:

  1. Let 
    query
     be the associated WorkerGlobalScope object's url's query.
  2. If 
    query
     is either null or the empty string, return the empty string.
  3. Return "?", followed by 
    query
    .

The 

hash

 attribute's getter must run these steps:

  1. Let 
    fragment
     be the associated WorkerGlobalScope object's url's fragment.
  2. If 
    fragment
     is either null or the empty string, return the empty string.
  3. Return "#", followed by 
    fragment
    .

 

 

=================================

=================================

=================================

 

 

 

참고링크:

 

http://javakorean.com/wp2/wp-content/uploads/2014/11/2014-HTML5-%EC%9D%B8%ED%84%B0%EB%84%B7%EB%B3%B4%EC%B6%A9%ED%95%99%EC%8A%B5%EC%9E%90%EB%A3%8C-15%EA%B0%95.pdf

 

http://boxfoxs.tistory.com/294

 

http://m.mkexdev.net/52

 

https://www.slideshare.net/jidolstar/html5-web-worker

 

http://www.sqler.com/398216

 

http://writingdeveloper.tistory.com/222

 

 

=================================

=================================

=================================

 

 

 

반응형
그리드형


관련글 더보기

댓글 영역