현재 번역은 완벽하지 않습니다. 한국어로 문서 번역에 동참해주세요.
웹 워커는 웹 컨텐츠를 위해서 백그라운드 스레드에서 스크립트를 실행할 간편한 방법을 제공합니다. 워커 스레드는 사용자 인터페이스(UI)를 방해하지 않고 작업을 수행할 수 있습니다. 또한 워커는 ( responseXML
과 channel
속성이 언제나 null이지만) XMLHttpRequest
를 사용하여 I/O작업을 수행할 수도 있습니다. 워커는 생성이 된 후에 생성자가 명시한 이벤트 핸들러로 메세지를 올려서 자신의 하위 작업(spawning task)에 메세지를 전달할 수 도 있습니다. 본 글에서 전용 워커와 공유 워커에 대하여 소개합니다.
A dedicated worker is only accessible from the script that first spawned it, whereas a shared workers can be accessed from multiple scripts.
Workers, run within a global context different from the current window (using the window
shortcut instead of self
in order to get the current global scope within a Worker
will return an error).
Note: See The Web Workers API landing page for reference documentation on workers and additional guides.
Simple demos
To make the basics of workers easy to grasp, we've written some basic demos for dedicated and shared workers:
- Basic dedicated worker example (run dedicated worker): 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.
- Basic shared worker example (run shared worker): Very similar, 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.
The demos both look something like this:
Let's move on and look at the basics of dedicated and shared workers
Dedicated workers
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.
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):
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.
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.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.Terminating a worker
If you need to immediately terminate a running worker, 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.
Workers may close themselves by calling their own close
method:
close();
Handling errors
When a runtime error occurs in 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 in the same domain into their scope. 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 */
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.
importScripts()
. This is done synchronously; importScripts()
does not return until all the scripts have been loaded and executed.Shared workers
A shared worker is accessible by multiple scripts, as long as they are on the same origin. In this section we'll discuss the JavaScript found in our Basic shared worker example; we'll concentrate on the differences between dedicated and shared workers.
Spawning a shared worker
Spawning a new worker is pretty much the same as with a dedicated worker, but with a different constructor name (index.html):
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 communicate with the worker through (this is done implicitly in the case of dedicated workers). This needs to be started with the start()
method before any messages can be posted (see the start of both multiply.js and square.js):
myWorker.port.start();
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,2]); console.log('Message posted to worker'); }
Note that here we are passing an extra value in the message, at the end of the array — if the data is for a multiplication it is a 1; if it is a squaring operation it is for a 2. This is so that we know where to display the answer when it comes back from the worker — there is no way to tell otherwise, as the worker doesn't keep a record of where it received a message from.
Now on to the worker — there is a bit more complication 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]); var resultType = e.data[2]; port.postMessage([workerResult,resultType]); } port.start(); }
First we use an onconnect
handler to fire code when a connection to the port happens (i.e. when the start()
method was invoked and a message was sent.) We use the ports
attribute of this event object to grab the port and store it in a variable. Next, we use an onmessage
handler on the port to do the calculation and return the result to the main thread. Note that in the message back to the main thread we are passing the result type (the number 1 or 2 to signify a multiplication or squaring operation, as explained earlier) as well as the result of the calculation.
Finally, back in the main script we deal with the message
myWorker.port.onmessage = function(e) { if(e.data[1] == 1) { result1.textContent = e.data[0]; console.log('Message received from worker'); } else if(e.data[1] == 2) { result2.textContent = e.data[0]; 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.
What can you do in a worker?
You can use most standard JavaScript features inside a web worker, such as:
Navigator
XMLHttpRequest
Array
,Date
,Math
, andString
Window.requestAnimationFrame
,WindowTimers.setTimeout
, andWindowTimers.setInterval
The main thing you can't do in a Worker is directly affect the parent page, including manipulating the DOM, and using that page's objects. You have to do it indirectly — sending a message back to the main script via DedicatedWorkerGlobalScope.postMessage
and then actioning the changes from there.
Note: For a complete list of functions available to workers, see Functions and interfaces available to workers.
About thread safety
The Worker
interface spawns real OS-level threads, and concurrency can cause interesting effects in your code if you aren't careful. However, in the case of web workers, the carefully controlled communication points with other threads means that it's actually very hard to cause concurrency problems. There's no access to non-thread safe 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.
Specifications
Specification | Status | Comment |
---|---|---|
WHATWG HTML Living Standard | Living Standard | No change from Web Workers. |
Web Workers | Editor's Draft | Initial definition. |
Browser compatibility
Feature | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari (WebKit) |
---|---|---|---|---|---|
Basic support | 4 | Unknown (3.5) | 10.0 | 10.6 | 4 |
Shared workers | 4 | 29 (29) | No support | 10.6 | 4 |
Passing data using structured cloning | 13 | 8 (8) | 10.0 | 11.5 | 6 |
Passing data using transferable objects | 17 webkit 21 |
18 (18) | No support | 15 | 6 |
Global URL |
10 as webkitURL 23 |
21 (21) | 11 | 15 | 6 as webkitURL |
Feature | Android | Chrome Mobile | Firefox Mobile (Gecko) | Firefox OS (Gecko) | IE Phone | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|---|
Basic support | 4.4 | 4 | 3.5 | 1.0.1 | 10.0 | 11.5 | 5.1 |
Shared workers | — | 4 | 8 | 1.0.1 | No support | — | — |
Passing data using structured cloning | — | 4 | 8 | 1.0.1 | No support | — | — |
Passing data using transferable objects | — | — | 18 | 1.0.1 | No support | — | — |
Browser notes
- Chrome/Opera give an error "
Uncaught SecurityError: Failed to construct 'Worker': Script at 'file:///Path/to/worker.js' cannot be accessed from origin 'null'.
" when you try to run a worker locally. It needs to be on a proper domain. - Safari doesn't let you run
console.log
from inside a worker.
See also
Worker
interfaceSharedWorker
interface- Functions available to workers
- Advanced concepts and examples