Web applications have become increasingly complex over the years, making it difficult for browsers to handle all processing required to run them smoothly. One way to improve performance is to use Web Workers, a JavaScript feature that allows you to run scripts in the background without interfering with the user interface. In this blog post, we'll explore how to use Web Workers in JavaScript to improve performance and create more responsive user interface. Also we will write simple example how to use Web Workers in React.
What are Web Workers?
Web Workers are a feature that allow JavaScript code to run in separate background threads. In traditional web development, JavaScript code runs in the same thread as the user interface, which means that long-running or computationally expensive tasks can block the UI and make the web page less responsive.
Web Workers solve this problem by allowing developers to offload these tasks to a separate thread, freeing up the main UI thread to respond to user input and keep the web page responsive.
There are two types of Web Workers in JavaScript: Dedicated Web Workers and Shared Web Workers. Dedicated Web Workers are used for tasks that are specific to a single instance of a web page, while Shared Web Workers can be shared across multiple instances of a web page.
Important thing is that Web Worker run in another global context that is different from the current window. This means that workers can't access the DOM (the window, document, page elements, etc.).
Beside aforementioned Web Worker, there is a special kind of Worker called Service Worker which runs in a separate thread, but there are some important differences between them. Service Workers are designed to run in the background and handle network requests, cache data, and provide offline functionality. This worker is more complex and we will write about him in some separate blog after we get some basics of Web Workers 😊.
So, next in this blog we will write examples using Dedicated Web Workers.
Dedicated Web Worker in JavaScript
Working with Dedicated Web Worker basically means instantiation of Worker object and calling appropriate methods and handling events. Here's an example of how to create a simple Dedicated Web Worker in JavaScript:
// create a new Web Worker
const worker = new Worker('worker.js');
// send a message to the Web Worker
worker.postMessage('Hello Web Worker');
// receive a message from the Web Worker
worker.onmessage = (event) => {
console.log(`Received message: ${event.data}`);
};
In this example, we're creating a new Web Worker by passing a URL to the Worker constructor. This URL points to a separate JavaScript file, named worker.js, which contains the code that will be executed in the Web Worker.
We then send a message to the Web Worker using the postMessage method, and listen for messages from the Web Worker using the onmessage event handler.
Here's an example of what the worker.js file look like:
// listen for messages from the main thread
onmessage = (event) => {
console.log(`Received message: ${event.data}`);
// do some heavy computation
const result = fibonacci(40);
// send the result back to the main thread
postMessage(`Result: ${result}`);
};
function fibonacci(n) {
if (n <= 1) {
return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
In worker.js, we're listening for messages from the main thread using the onmessage event handler. When a message is received, we do some heavy computation (in this case, calculating the 40th number in the Fibonacci sequence) and send the result back to the main thread using the postMessage method.
Dedicated Web Worker in React
In the context of React, Web Workers can be used to improve the performance of complex components or to perform tasks that are not directly related to rendering the UI. By offloading these tasks to a Web Worker, we can ensure that the main UI thread remains responsive and the user has a better experience overall.
Here's an example of how to use a Web Worker in a React:
import React, { useState, useEffect } from 'react';
function FibonacciCalculator() {
const [result, setResult] = useState(null);
const [worker, setWorker] = useState(null);
useEffect(() => {
const w = new Worker(`${process.env.PUBLIC_URL}/worker.js`);
setWorker(w);
w.onmessage = (event) => {
setResult(event.data);
};
// When the component unmounts, terminate the worker to prevent memory leaks
return () => w.terminate();
}, []);
function calculateFibonacci() {
// send a message to the Web Worker
worker.postMessage(40);
}
return (
<div>
<button> onclick="{calculateFibonacci}">Calculate Fibonacci</button>
{result && <p>The 40th number in the Fibonacci sequence is {result}.</p>}
</div>
);
export default FibonacciCalculator;
In this example, we're creating a new Web Worker using useEffect hook and storing reference in a state. We're also setting up a cleanup function to terminate the worker when the component unmounts -w.terminate().
When the user clicks the "Calculate Fibonacci" button, we send a message to the Web Worker using the postMessage method, passing in the number 40 as the argument and listen for messages from the worker using the onmessage event handler added in useEffect hook. When a message is received, result is updated using the setResult function.
process.env.PUBLIC_URL is path to out public folder and we will put our worker.js file there.
Here's a content of the worker.js file for React sample:
// listen for messages from the main thread
onmessage = (event) => {
// do some heavy computation
const result = fibonacci(event.data);
// send the result back to the main thread
postMessage(result);
};
function fibonacci(n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
Web Workers and error handling
As we have onmessage event, we also can use onerror event for error handling. When a runtime error occurs in the worker, onerror event handler is called.
The error event has the following three fields:
- 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)
worker.onerror = (error) => {
console.log("error: ", error);
};
Conclusion
In conclusion, Web Workers are an essential part of modern web development, allowing developers to run computationally intensive tasks in the background without blocking the main thread and causing UI freezes or slowdowns.
Web Workers come in two types, Shared Workers and Dedicated Workers, each with their own strengths and use cases.
Using Web Workers can be a powerful way to improve performance and provide a better user experience. With a few simple steps, you can create a Dedicated Web Worker in a separate file and communicate with it using the postMessage method and the onmessage event handler.
Error handling is an important aspect of any application, and with Web Workers we can use the onerror event handler to efficiently handle errors.