Using Web Workers - Web APIs | MDN
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers • 198 KB fetched
Open original page
Using Web Workers - Web APIs | MDN
*
Skip to main content
*
Skip to search
HTML
HTML: Markup language
HTML reference
*
Elements
*
Global attributes
*
Attributes
*
See all…
HTML guides
*
Responsive images
*
HTML cheatsheet
*
Date & time formats
*
See all…
Markup languages
*
SVG
*
MathML
*
XML
CSS
CSS: Styling language
CSS reference
*
Properties
*
Selectors
*
At-rules
*
Values
*
See all…
CSS guides
*
Box model
*
Animations
*
Flexbox
*
Colors
*
See all…
Layout cookbook
*
Column layouts
*
Centering an element
*
Card component
*
See all…
JavaScript JS
JavaScript: Scripting language
JS reference
*
Standard built-in objects
*
Expressions & operators
*
Statements & declarations
*
Functions
*
See all…
JS guides
*
Control flow & error handing
*
Loops and iteration
*
Working with objects
*
Using classes
*
See all…
Web APIs
Web APIs: Programming interfaces
Web API reference
*
File system API
*
Fetch API
*
Geolocation API
*
HTML DOM API
*
Push API
*
Service worker API
*
See all…
Web API guides
*
Using the Web animation API
*
Using the Fetch API
*
Working with the History API
*
Using the Web speech API
*
Using web workers
All
All web technology
Technologies
*
Accessibility
*
HTTP
*
URI
*
Web extensions
*
WebAssembly
*
WebDriver
*
See all…
Topics
*
Media
*
Performance
*
Privacy
*
Security
*
Progressive web apps
Learn
Learn web development
Frontend developer course
*
Getting started modules
*
Core modules
*
MDN Curriculum
*
Check out the video course from Scrimba, our partner
Learn HTML
*
Structuring content with HTML module
Learn CSS
*
CSS styling basics module
*
CSS layout module
Learn JavaScript
*
Dynamic scripting with JavaScript module
Tools
Discover our tools
*
Playground
*
HTTP Observatory
*
Border-image generator
*
Border-radius generator
*
Box-shadow generator
*
Color format converter
*
Color mixer
*
Shape generator
About
Get to know MDN better
*
About MDN
*
Advertise with us
*
Community
*
MDN on GitHub
Blog
Toggle sidebar
*
Web
*
Web APIs
*
Web Workers API
*
Using Web Workers
Theme
*
OS default
*
Light
*
Dark
English (US)
Remember language
Learn more
*
Deutsch
*
English (US)
*
Español
*
Français
*
日本語
*
Русский
*
中文 (简体)
*
正體中文 (繁體)
Using Web Workers
Web Workers are a simple means for web content to run scripts in background threads. The worker thread can perform tasks without interfering with the user interface. In addition, they can make network requests using the fetch() or XMLHttpRequest APIs. Once created, a worker can send messages to the JavaScript code that created it by posting messages to an event handler specified by that code (and vice versa).
This article provides a detailed introduction to using web workers.
In this article
*
Web Workers API
*
Dedicated workers
*
Shared workers
*
About thread safety
*
Content security policy
*
Transferring data to and from workers: further details
*
Embedded workers
*
Further examples
*
Other types of workers
*
Debugging worker threads
*
Functions and interfaces available in workers
*
Specifications
*
See also
Web Workers API
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 . 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 message event'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 can make network requests using the fetch() or XMLHttpRequest APIs (although note that the responseXML attribute of XMLHttpRequest will always be null ).
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 ( 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 ):
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 ):
js
const myWorker = new Worker("worker.js");
Note:
Bundlers, including webpack , Vite , and Parcel , recommend passing URLs that are resolved relative to import.meta.url to the Worker() constructor. For example:
js
const myWorker = new Worker(new URL("worker.js", import.meta.url));
This way, the path is relative to the current script instead of the current HTML page, which allows the bundler to safely do optimizations like renaming (because otherwise the worker.js URL may point to a file not controlled by the bundler, so it cannot make any assumptions).
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 ):
js
[first, second].forEach((input) => {
input.onchange = () => {
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 ):
js
onmessage = (e) => {
console.log("Message received from main script");
const 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 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:
js
myWorker.onmessage = (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:
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:
js
myWorker.terminate();
The worker thread is killed immediately.
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 the following examples are valid:
js
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 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 workers
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 ( Firefox bug 1177621 ).
Spawning a shared worker
Spawning a new shared 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:
js
const 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. Calling start() is only needed if the message event is wired up via the addEventListener() method.
Note:
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.
Shared worker lifetime
Shared workers are shut down when they are no longer referenced by any windows, iframes, or workers.
Browsers may keep workers alive between same-origin navigations 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.
The extendedLifetime constructor option may also be specified to keep a shared worker alive for a short period after all references to it have closed:
js
const worker = new SharedWorker("worker.js", { extendedLifetime: true });
This allows work to be done after the user navigates away from the page, such as writing state information to storage, or sending analytics data back to servers.
This is more ergonomic than using a service worker for the same purpose.
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 ):
js
squareNumber.onchange = () => {
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 ):
js
onconnect = (e) => {
const port = e.ports[0];
port.onmessage = (e) => {
const 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 set up, 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 an onmessage handler on the port to do the calculation and return the result to the main thread. Setting up this onmessage handler 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 ):
js
myWorker.port.onmessage = (e) => {
result2.textContent = e.data;
console.log("Message received from worker");
};
When a message comes back through the port from the worker, we insert the calculation result inside the appropriate result paragraph.
About thread safety
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-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.
Content security policy
Workers are considered to have their own execution context, distinct from the document that created them. For this reason 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:
http
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 that created it.
Transferring data to and from workers: further details
Data passed between the main page and workers is copied , not shared (except for certain objects that can be explicitly 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 .
As you probably know by now, data is exchanged between the two threads via messages using postMessage() , and the message event's data attribute contains data passed back from the worker.
example.html : (the main page):
js
const myWorker = new Worker("my_task.js");
myWorker.onmessage = (event) => {
console.log(`Worker said : "${event.data}"`);
};
myWorker.postMessage({ lastUpdate: new Date() });
my_task.js (the worker):
js
self.onmessage = (event) => {
postMessage(`Last updated: ${event.data.lastUpdate.toDat
Links found on this page
- Skip to main content [direct]
- HTML: Markup language [direct]
- Elements [direct]
- Global attributes [direct]
- Attributes [direct]
- See all… [direct]
- Responsive images [direct]
- HTML cheatsheet [direct]
- Date & time formats [direct]
- See all… [direct]
- SVG [direct]
- MathML [direct]
- XML [direct]
- CSS: Styling language [direct]
- Properties [direct]
- Selectors [direct]
- At-rules [direct]
- Values [direct]
- See all… [direct]
- Box model [direct]
- Animations [direct]
- Flexbox [direct]
- Colors [direct]
- See all… [direct]
- Column layouts [direct]
- Centering an element [direct]
- Card component [direct]
- See all… [direct]
- JavaScript: Scripting language [direct]
- Standard built-in objects [direct]
- Expressions & operators [direct]
- Statements & declarations [direct]
- Functions [direct]
- See all… [direct]
- Control flow & error handing [direct]
- Loops and iteration [direct]
- Working with objects [direct]
- Using classes [direct]
- See all… [direct]
- Web APIs: Programming interfaces [direct]
- File system API [direct]
- Fetch API [direct]
- Geolocation API [direct]
- HTML DOM API [direct]
- Push API [direct]
- Service worker API [direct]
- Using the Web animation API [direct]
- Using the Fetch API [direct]
- Working with the History API [direct]
- Using the Web speech API [direct]
- All web technology [direct]
- Accessibility [direct]
- HTTP [direct]
- URI [direct]
- Web extensions [direct]
- WebAssembly [direct]
- WebDriver [direct]
- Media [direct]
- Performance [direct]
- Privacy [direct]
- Security [direct]
- Progressive web apps [direct]
- Learn web development [direct]
- Getting started modules [direct]
- Core modules [direct]
- MDN Curriculum [direct]
- Check out the video course from Scrimba, our partner [direct]
- Structuring content with HTML module [direct]
- CSS styling basics module [direct]
- CSS layout module [direct]
- Dynamic scripting with JavaScript module [direct]
- Playground [direct]
- HTTP Observatory [direct]
- Border-image generator [direct]
- Border-radius generator [direct]
- Box-shadow generator [direct]
- Color format converter [direct]
- Color mixer [direct]
- Shape generator [direct]
- About MDN [direct]