JavaScript Web Workers
Until now, all examples in this tutorial run on the browser's main thread.
Asynchronous programming keeps applications responsive while waiting for:
- Timers
- User input
- Network requests
However, long-running calculations can still block the main thread and make a page unresponsive.
Web Workers solve this by running JavaScript in a separate thread.
Why Web Workers?
JavaScript normally executes one task at a time.
If a task takes a long time to finish, the browser cannot respond to user input until the task has completed.
This can make a page appear frozen.
The page becomes unresponsive until the calculation finishes.
Example
A long-running calculation freezes the page.
function calculate() {
let total = 0;
for (let i = 0; i < 5e9; i++) {
total += i;
}
return total;
}
myDisplayer("Calculating...");
let result = calculate();
myDisplayer(result);
Try it Yourself »
Can Asynchronous Programming Help?
Timers, Promises, and async/await do not solve this problem.
They all execute JavaScript on the same main thread.
If the thread is busy performing calculations, the browser must still wait.
Example
myDisplayer("Start");
setTimeout(function () {
myDisplayer("Timer")}, 1000);
let i = 5e9;
while (--i > 0);
myDisplayer("Finished");
The timer finishes after one second, but its callback cannot run until the loop has completed.
Try it Yourself »What Is a Web Worker?
A Web Worker runs JavaScript in a separate thread.
While the worker performs calculations, the browser's main thread remains free to respond to user input.
The main thread and the worker communicate by sending messages.
Browser Support
Before creating a worker, check whether the browser supports Web Workers.
Example
if (typeof(Worker) !== "undefined") {
myDisplayer("Web Workers are supported.");
} else {
myDisplayer("Web Workers are not supported.");
}
Try it Yourself »
Creating a Worker
Create a worker with the new Worker() constructor.
const worker = new Worker("worker.js");
The file passed to the constructor contains the code that will run in the worker thread.
Receiving Messages
The main thread receives messages with the onmessage event.
Example
const worker = new Worker("worker.js");
worker.onmessage = function(event) {
myDisplayer(event.data);
};
Try it Yourself »
Sending Messages
The main thread sends data to the worker with postMessage().
Main page:
const worker = new Worker("worker.js");
worker.postMessage(25);
worker.js
onmessage = function(event) {
postMessage(event.data * event.data);
}
Result
625
Complete Example
Example
const worker = new Worker("worker.js");
worker.onmessage = function(event) {
myDisplayer(event.data);
};
worker.postMessage(1000000);
worker.js
onmessage = function(event) {
let sum = 0;
for (let i = 0; i <= event.data; i++) {
sum += i;
}
postMessage(sum);
};
Note
The calculation happens in the worker.
The page remains responsive.
Part 1 Summary
- Web Workers run JavaScript in a separate thread.
- Workers keep the main thread responsive during long-running calculations.
- Create a worker with
new Worker(). - Workers communicate using
postMessage(). - Receive messages with the
onmessageevent.
Web Workers Part 2
Passing Data to a Worker
The postMessage() method can send numbers, strings, arrays, and objects.
The browser copies the data and sends the copy to the worker.
Main page:
const worker = new Worker("worker.js");
const person = {
name: "John",
age: 30
};
worker.postMessage(person);
worker.onmessage = function(event) {
document.getElementById("demo").innerHTML = event.data;
};
worker.js:
onmessage = function(event) {
const person = event.data;
const message = person.name + " is " + person.age + " years old.";
postMessage(message);
};
Note
The worker receives the object through event.data.
Worker Progress Messages
A worker can send more than one message.
This makes it possible to report progress while a long calculation is running.
Main page:
const worker = new Worker("worker.js");
worker.onmessage = function(event) {
document.getElementById("demo").innerHTML = event.data;
};
worker.postMessage(100000000);
worker.js:
onmessage = function(event) {
const limit = event.data;
let total = 0;
for (let i = 0; i <= limit; i++) {
total += i;
if (i % 10000000 === 0) {
postMessage("Progress: " + Math.round(i / limit * 100) + "%");
}
}
postMessage("Result: " + total);
};
Note
The worker sends progress messages without blocking the main page.
The main thread can still respond to buttons, scrolling, and other user actions while the worker is running.
Starting a Worker
A worker can be created when the user starts a task.
Saving the worker in a variable makes it possible to stop or reuse the reference later.
Example
let worker;
function startWorker() {
worker = new Worker("worker.js");
worker.onmessage = function(event) {
document.getElementById("demo").innerHTML = event.data;
};
worker.postMessage(100000000);
}
Stopping a Worker
Use the terminate() method to stop a worker immediately.
worker.terminate();
Note
After a worker has been terminated, it cannot be started again.
Create a new worker if the task must run again.
Example
let worker;
function startWorker() {
if (worker) {
return;
}
worker = new Worker("worker.js");
worker.onmessage = function(event) {
document.getElementById("demo").innerHTML = event.data;
};
worker.postMessage(100000000);
}
function stopWorker() {
if (worker) {
worker.terminate();
worker = undefined;
document.getElementById("demo").innerHTML = "Worker stopped";
}
}
Complete Start and Stop Example
This example starts a long calculation in a worker.
The worker reports progress and can be stopped before it finishes.
Example
Main page:
<button onclick="startWorker()">Start Worker</button>
<button onclick="stopWorker()">Stop Worker</button>
Ready
let worker;
function startWorker() {
if (worker) {
return;
}
document.getElementById("demo").innerHTML = "Starting...";
worker = new Worker("worker.js");
worker.onmessage = function(event) {
document.getElementById("demo").innerHTML = event.data;
if (event.data.startsWith("Result:")) {
worker.terminate();
worker = undefined;
}
};
worker.postMessage(100000000);
}
function stopWorker() {
if (worker) {
worker.terminate();
worker = undefined;
document.getElementById("demo").innerHTML = "Worker stopped";
}
}
worker.js:
onmessage = function(event) {
const limit = event.data;
let total = 0;
for (let i = 0; i <= limit; i++) {
total += i;
if (i % 10000000 === 0) {
const percent = Math.round(i / limit * 100);
postMessage("Progress: " + percent + "%");
}
}
postMessage("Result: " + total);
};
Worker Errors
Use the onerror event to handle errors from a worker.
Example
const worker = new Worker("worker.js");
worker.onerror = function(error) {
document.getElementById("demo").innerHTML =
"Error: " + error.message;
};
The error object can contain information such as the message, filename, and line number.
| Property | Description |
|---|---|
message |
The error message. |
filename |
The file where the error occurred. |
lineno |
The line number where the error occurred. |
colno |
The column number where the error occurred. |
Handling Errors Inside a Worker
A worker can also catch its own errors and send a message to the main thread.
Main page:
worker.onmessage = function(event) {
if (event.data.error) {
document.getElementById("demo").innerHTML = event.data.error;
} else {
document.getElementById("demo").innerHTML = event.data.result;
}
};
worker.js:
onmessage = function(event) {
try {
const result = calculate(event.data);
postMessage({result: result});
} catch(error) {
postMessage({error: error.message});
}
};
Closing a Worker from Inside
A worker can stop itself by calling close().
postMessage("Finished");
close();
The close() function stops the worker after the current code finishes.
| Method | Called From | Description |
|---|---|---|
worker.terminate() |
Main thread | Stops the worker immediately. |
close() |
Worker thread | Lets the worker stop itself. |
Creating a New Worker
A terminated worker cannot be restarted.
Create a new Worker object to run the task again.
worker = new Worker("worker.js");
Set the worker variable to undefined or null after termination.
This makes it easier to know whether a worker is currently running.
One Worker or Many Workers?
A page can create more than one worker.
const worker1 = new Worker("worker1.js");
const worker2 = new Worker("worker2.js");
Each worker runs in a separate thread.
However, creating many workers can use significant memory and processing power.
Use workers only for tasks that benefit from running in a separate thread.
Do not create a new worker for every small calculation.
Worker Lifecycle
A Web Worker normally follows these steps:
- The main page creates the worker.
- The main page sends data with
postMessage(). - The worker receives the data.
- The worker performs its task.
- The worker sends results back.
- The main page uses the result.
- The worker is terminated when it is no longer needed.
Create Worker
|
|
v
Send Data
|
|
v
Worker Calculates
|
|
v
Send Result
|
|
v
Stop Worker
Part 2 Summary
- Workers can receive strings, numbers, arrays, and objects.
- A worker can send multiple messages to report progress.
- Use
terminate()to stop a worker from the main thread. - Use
close()to stop a worker from inside the worker. - Use
onerrorto handle worker errors. - A terminated worker cannot be restarted.
- Create a new Worker object to run the task again.
What Can a Worker Do?
A Web Worker runs JavaScript just like the main thread.
It can perform calculations, use timers, make network requests, and work with JavaScript objects.
This makes workers useful for CPU-intensive tasks that would otherwise block the page.
| Task | Supported |
|---|---|
| JavaScript calculations | Yes |
| Loops | Yes |
| Functions | Yes |
| Arrays and Objects | Yes |
| Promises | Yes |
| fetch() | Yes |
| setTimeout() | Yes |
| setInterval() | Yes |
What Can't a Worker Do?
A worker cannot access the web page.
Only the main thread can modify the DOM.
| Feature | Available |
|---|---|
document |
No |
window |
No |
| HTML elements | No |
| DOM events | No |
| alert() | No |
A worker performs calculations.
The main thread updates the user interface.
Updating the Page
A worker cannot update HTML directly.
Instead, it sends the result back to the main thread.
Example
worker.js:
postMessage("Finished");
Main page:
worker.onmessage = function(event) {
document.getElementById("demo").innerHTML = event.data;
};
Using fetch() Inside a Worker
A worker can download data from a server.
The downloaded data can then be sent back to the main thread.
Example
worker.js:
async function loadData() {
const response = await fetch("data.txt");
const text = await response.text();
postMessage(text);
}
loadData();
Try it Yourself »
Using Timers
Workers support JavaScript timers.
Example
setInterval(function() {
postMessage(new Date().toLocaleTimeString());
}, 1000);
When Should You Use a Worker?
Workers are useful for long-running JavaScript tasks.
| Good Uses |
|---|
| Large mathematical calculations |
| Image processing |
| Video processing |
| Searching large datasets |
| Compression and decompression |
| Encryption |
| Artificial Intelligence |
| Scientific calculations |
When Should You Not Use a Worker?
Workers are not needed for small tasks.
Creating a worker takes time and uses memory.
Simple calculations often run faster on the main thread.
Do not create a worker unless the task is large enough to benefit from another thread.
Asynchronous Programming vs Web Workers
Asynchronous programming and Web Workers solve different problems.
| Asynchronous Programming | Web Workers |
|---|---|
| One JavaScript thread | Multiple JavaScript threads |
| Good for waiting | Good for calculating |
| Uses Promises and async/await | Uses Worker objects |
| Network requests | CPU-intensive tasks |
| Does not block while waiting | Does not block while calculating |
Asynchronous programming keeps JavaScript busy while waiting.
Web Workers keep the browser responsive while JavaScript is calculating.
Common Mistakes
Trying to Access the DOM
This will fail:
document.getElementById("demo").innerHTML = "Hello";
Workers must use postMessage() instead.
Creating Too Many Workers
Workers consume memory and CPU resources.
Create only the workers you need.
Forgetting to Stop Workers
Terminate workers when they are no longer needed.
worker.terminate();
Summary
- Web Workers run JavaScript on a separate thread.
- Workers keep the main thread responsive during long calculations.
- Workers communicate with the main thread using
postMessage(). - Workers cannot access the DOM.
- Workers support Promises,
fetch(), and timers. - Use workers for CPU-intensive tasks.
- Use asynchronous programming for waiting on operations such as timers and network requests.
Congratulations!
You have completed the JavaScript Asynchronous Programming tutorial.
You have learned how JavaScript handles asynchronous operations using:
- Callbacks
- Promises
- async / await
- The Event Loop
- Timers
- Fetch API
- AbortController
- Web Workers
You are now ready to build responsive JavaScript applications that can perform asynchronous operations and long-running calculations efficiently.